Compare commits

...

782 Commits

Author SHA1 Message Date
topjohnwu
b885ccbd63 Bump version 2018-07-08 06:56:29 +08:00
Vv2233Bb
da6f1d0f12 Update to values.lt 2018-07-08 06:56:29 +08:00
Rom
4c0d435b6b Little adjustment for French translation :) 2018-07-07 01:39:35 +08:00
topjohnwu
0e717a2de4 Fix additional setup 2018-07-06 01:57:32 +08:00
topjohnwu
cada862214 Fix install script to copy folders 2018-07-05 17:29:44 +08:00
topjohnwu
682c6d4e7b Prettier notification text 2018-07-05 03:21:41 +08:00
topjohnwu
d0a253c97e Switch to discussion thread 2018-07-05 03:15:10 +08:00
topjohnwu
c0e2b3027b Add Trad. Chinese stub translations 2018-07-05 03:04:12 +08:00
Rom
e7dc14b07d Update French translation 2018-07-05 03:00:53 +08:00
topjohnwu
0da9146e90 Cleanup resources and add Trad. Chinese translation 2018-07-05 02:56:37 +08:00
topjohnwu
ad05a33e02 Show release notes and changelog in MarkDownWindow 2018-07-05 02:02:37 +08:00
Oliver Cervera
8224e038a3 added latest strings
Added latest strings 
- setup_title
- setup_msg
- restore_img_title
- restore_img_msg
2018-07-04 23:38:44 +08:00
topjohnwu
03c04c2141 Prevent duplicate policy of same package name
Fix #470
2018-07-04 23:38:09 +08:00
topjohnwu
2e091b04e5 Sort hidden apps to the top 2018-07-04 21:15:26 +08:00
Taras Korzhak
60296493fe update Ukrainian strings 2018-07-04 20:22:14 +08:00
Ilya Kushnir
20c20f8f9b Update RU strings 2018-07-04 20:22:03 +08:00
Jonas Schubert
f1d642a4e5 Updated german full/res strings 2018-07-04 20:21:49 +08:00
vvb2060
e0e5ea17a4 Update zh-rCN translation 2018-07-04 20:21:32 +08:00
Igor Sorocean
91a0ba72dc add romanian translation for stub 2018-07-04 20:21:22 +08:00
Albert I
c54c5a974a full: Update Indonesian translations
Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-07-04 20:21:08 +08:00
dark-basic #DarkBasic BasicHD
532b8c54ab Update Strings.xml Full Version 2018-07-04 20:20:57 +08:00
Eray Rafet
5ac87891b5 Update Bulgarian translation 2018-07-04 20:20:38 +08:00
topjohnwu
2d905ce3fb Don't popup changelogs on launch 2018-07-04 20:19:51 +08:00
topjohnwu
831112abd2 Hide install to second slot option until Magisk properly supports it 2018-07-04 20:18:17 +08:00
topjohnwu
153d0f5505 Small optimization to UpdateRepos 2018-07-04 20:13:12 +08:00
topjohnwu
c78896a335 Get rid of error logs 2018-07-04 18:11:57 +08:00
topjohnwu
316ec98e0f Rewrite Magisk log fragment 2018-07-04 17:59:16 +08:00
topjohnwu
cf58545a45 Move shell code into scripts 2018-07-04 17:15:26 +08:00
topjohnwu
e86015badc New uninstallation method 2018-06-27 05:58:56 +08:00
topjohnwu
c8f65fc9a1 Fix selinux error while installing Magisk on some devices 2018-06-27 01:08:48 +08:00
topjohnwu
7684602ea8 More fixes for non-root devices 2018-06-26 06:04:11 +08:00
topjohnwu
4601989d4a Speed up startup time 2018-06-26 00:29:01 +08:00
topjohnwu
23f697d62b Fix non-root boot patching 2018-06-25 19:46:41 +08:00
topjohnwu
4ff39f8817 Update to libsu 1.2.0 2018-06-20 04:48:56 +08:00
linar10
1df41003ec Update strings.xml 2018-06-20 04:48:56 +08:00
linar10
1f39ee41ad Create strings.xml 2018-06-20 04:48:56 +08:00
Rom
42d8b1ecb9 Update French translation 2018-06-20 04:48:56 +08:00
dark-basic #DarkBasic BasicHD
a4da7b33e6 Create Strings.xml ver. Stub 2018-06-20 04:48:56 +08:00
Oliver Cervera
e4ee9e9095 Create Stub for Italian (it) 2018-06-20 04:48:56 +08:00
topjohnwu
77430a282f Support new util_functions.sh 2018-06-18 01:40:42 +08:00
topjohnwu
e6c1dd532d Re-implement duplicate Magisk Manager logic
Starting from the next Magisk release, it will no longer prefer the package name com.topjohnwu.magisk over a hidden manager; it will always be aware whether the hidden manager exists, so when a package named com.topjohnwu.magisk is installed alongside with the hidden manager, com.topjohnwu.magisk will not have root access by default.
This will prevent malware from using the package name com.topjohnwu.magisk to gain root access when a user is using a hidden manager.
To support this new behavior, several changes has to be done:
- Never grant com.topjohnwu.magisk in Magisk Manager (if it IS the actual manager, MagiskSU will grant it by default)
- While hidden, remove com.topjohnwu.magisk if exists
- Restore Magisk Manager (unhide) has to be done with root
- Upgrading Magisk Manager should preserve package name (implemented in a949641)
2018-06-14 04:30:24 +08:00
topjohnwu
d1f301e059 Improve stub manager 2018-06-14 02:31:31 +08:00
topjohnwu
1e812c40ce Finally fix magisk icons 2018-06-12 19:26:34 +08:00
topjohnwu
a949641342 Preserve hidden when upgrade 2018-06-12 05:32:35 +08:00
topjohnwu
c231e88a5d Small tweak in setting up magisk DB 2018-06-12 00:04:36 +08:00
topjohnwu
79c71509f6 Add NoUIActivity 2018-06-10 14:51:37 +08:00
topjohnwu
5dab580cfc Move translation to correct location 2018-06-10 11:56:23 +08:00
topjohnwu
499a157946 Update snet extension 2018-06-10 00:43:01 +08:00
topjohnwu
c5a7ab2415 Move runWithPermission method 2018-06-09 17:14:24 +08:00
Fatih Fırıncı
3dd5a6f378 Create strings.xml 2018-06-09 15:49:37 +08:00
Jonas Schubert
7be26a0677 Added german strings for stub 2018-06-09 15:49:24 +08:00
vvb2060
c183fdd3ca add zh-rCN translation 2018-06-09 15:49:06 +08:00
Rom
baa439457e Minor French translation update 2018-06-09 15:48:58 +08:00
Albert I
4dbcd54b72 Initial stub app translation to Indonesian
Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-06-09 15:48:42 +08:00
Eray Rafet
11062f2d4f Create strings.xml 2018-06-09 15:48:31 +08:00
topjohnwu
a0466085fe New permissions targeting SDK 28 2018-06-09 15:45:15 +08:00
topjohnwu
f2f7d77847 Fix language settings UI 2018-06-03 11:50:12 +08:00
topjohnwu
b2105f2d88 Optimize drawables 2018-06-03 04:41:45 +08:00
topjohnwu
4126f3bdcb Update README 2018-06-03 00:00:39 +08:00
topjohnwu
74ccfe6088 No more PNGs! 2018-06-02 23:12:02 +08:00
topjohnwu
48085b5573 Implement stub Magisk Manager 2018-06-02 22:00:52 +08:00
topjohnwu
7b9ddc9b3b Add new flavor: stub 2018-05-27 14:34:05 +08:00
vvb2060
15726a759c Update zh-rCN translation 2018-05-27 02:02:08 +08:00
Eray Rafet
2c7474ea87 Update Bulgarian translation 2018-05-27 02:01:53 +08:00
Taras
c726aee643 update Ukrainian translation 2018-05-27 02:01:38 +08:00
Eray Rafet
c3e94e1480 Create strings.xml
Add Bulgarian translation
2018-05-20 17:52:57 +08:00
Albert I
5f1343e5b4 values: Fix grammar
Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-05-20 17:52:57 +08:00
Albert I
ffb1303d61 values-in: Update Indonesian strings
* "Requires Additional Setup" strings have been added.
* Clean up translators string (RIP link)

Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-05-20 17:52:57 +08:00
Oliver Cervera
a0b0d938f0 New Italian strings
Added new translated strings
2018-05-20 17:52:57 +08:00
Fatih Fırıncı
158f5ba7d9 Update strings.xml 2018-05-20 17:52:57 +08:00
Rom
b8cf40161c Update French translation according to commit 630f2b7 2018-05-20 17:52:57 +08:00
dark-basic #DarkBasic BasicHD
fb96e6a56f Update strings.xml
New Lines added.
-The translation could suffer changes, after its implementation
---> Very good work topjohnwu ;D <------
2018-05-20 17:23:52 +08:00
Jonas Schubert
6668ba2511 Missing german setup toast translation added 2018-05-20 17:23:38 +08:00
topjohnwu
4668ef3020 Force shell usage in SuFile 2018-05-20 14:33:04 +08:00
topjohnwu
630f2b7d19 Support fixing Magisk environment 2018-05-13 18:14:10 +08:00
topjohnwu
dde0a4a7c8 Fix strings 2018-05-13 18:10:09 +08:00
Rom
b06f69573d Update French translation 2018-05-06 03:24:13 +08:00
topjohnwu
8fd03f7434 Optimize repo updates 2018-05-06 02:51:23 +08:00
Vv2233Bb
90e4ac2d23 Update strings.xml (Lt) 2018-05-05 12:29:09 +08:00
RoySchutte
956bceae75 Update strings.xml 2018-05-05 12:28:52 +08:00
Albert I
c663be86de values-in: Update Indonesian translation
* Added "Cannot check SafetyNet" strings.

Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-05-05 12:28:42 +08:00
linar10
aca78baecf Update strings.xml 2018-05-05 12:28:26 +08:00
Fatih Fırıncı
fbcf6b7954 Update strings.xml 2018-05-05 12:28:14 +08:00
Taras
84123222aa Ukrainian translation 2018-05-05 12:27:55 +08:00
Oliver Cervera
e9dbcf693d Update Italian strings 2018-05-05 12:27:39 +08:00
vvb2060
1cd0a9d48f Update zh-rCN translation 2018-05-05 12:27:18 +08:00
dark-basic #DarkBasic BasicHD
1b48e44914 Update strings.xml
Update
-New lines added.
2018-05-05 12:26:29 +08:00
Jonas Schubert
0a398f03fd updated german translation adding missing strings 2018-05-05 12:26:14 +08:00
topjohnwu
15ed3e52f2 Bump version 2018-04-29 14:50:08 +08:00
topjohnwu
8990919dab For some reason SN check cannot run on repackaged managers 2018-04-29 14:40:42 +08:00
topjohnwu
e5638e4b15 Prevent installing 32-bit binaries on older versions 2018-04-29 14:39:03 +08:00
topjohnwu
404c6fac9a We don't need to update APK if installed via manager 2018-04-29 14:34:59 +08:00
topjohnwu
267395bfa2 Set ranks in case cannot show all 2018-04-22 14:03:10 +08:00
topjohnwu
920fc5ae99 Only use 32-bit binaries 2018-04-22 13:54:27 +08:00
topjohnwu
92ed0ae51b Clean database more thoroughly 2018-04-22 13:53:25 +08:00
topjohnwu
6764a98409 SEPolicy updates 2018-04-14 18:08:53 +08:00
topjohnwu
fd7b5f393a Make Magisk version more clear 2018-04-14 15:32:37 +08:00
topjohnwu
2ca528f93f Fix typo 2018-04-08 03:04:31 +08:00
topjohnwu
ce2e6b7d35 Prevent outdated modules to be shown 2018-04-08 01:05:01 +08:00
topjohnwu
684c5d225a Optimize repo update 2018-04-07 04:47:22 +08:00
topjohnwu
b75018b03b Fix SN check errors on some devices 2018-04-07 01:49:22 +08:00
topjohnwu
41499d4b3c Improve back navigation 2018-04-07 01:40:20 +08:00
topjohnwu
383c97c303 Add app shortcuts for Android 7.1+ 2018-04-07 00:45:10 +08:00
topjohnwu
74b54ef371 Cleanup resources 2018-04-07 00:44:58 +08:00
Fatih Fırıncı
bbf7b4db79 Update strings.xml 2018-04-06 01:56:29 +08:00
feliph-rc
c61f0acab5 Update strings.xml 2018-04-06 01:56:22 +08:00
Vv2233Bb
398af123b2 Lithuanian update 2018-04-06 01:56:14 +08:00
topjohnwu
315fa9d7d3 Optimize magisk database handling 2018-04-06 01:54:09 +08:00
topjohnwu
fb5e8ef40c Improve handling of snet extention 2018-04-05 20:52:34 +08:00
topjohnwu
e79d764148 Bump version 2018-03-28 15:23:03 +08:00
Unknown
ebbee0dc43 Croatian translations
Fixed typos, updated some translations.
2018-03-28 15:14:31 +08:00
topjohnwu
ed0c16e201 Update for new lint 2018-03-28 02:36:03 +08:00
Rom
209fdf349a Update for French translation 2018-03-28 02:25:42 +08:00
Fatih Fırıncı
f49f2afacd Update strings.xml 2018-03-28 02:24:57 +08:00
wokija
8c6330a3c4 Update strings.xml
Corrected translations, typos
2018-03-28 02:24:45 +08:00
dark-basic #DarkBasic BasicHD
337b777125 Update strings.xml 2018-03-28 02:24:34 +08:00
topjohnwu
1b756e8d96 Remove SafetyNet default apps 2018-03-28 02:23:50 +08:00
topjohnwu
52d478df1a Cleanup build.gradle 2018-03-26 03:52:12 +08:00
topjohnwu
0c782edf21 Bump version 2018-03-18 12:25:13 +08:00
topjohnwu
e3948d295e Update fragment transaction 2018-03-18 12:25:13 +08:00
topjohnwu
5f2c742a5c Fix strings 2018-03-18 12:25:12 +08:00
Vv2233Bb
b30c77aab9 Update for values-lt 2018-03-17 20:22:09 +08:00
Albert I
a5916b9c49 values-in: Add missing translation
Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-03-17 20:21:52 +08:00
dark-basic #DarkBasic BasicHD
453180e30b Update strings.xml 2018-03-17 20:21:39 +08:00
linar10
8bd432d391 Update strings.xml 2018-03-17 20:21:21 +08:00
topjohnwu
c9d3e20aef Fix repo loading UI logic 2018-03-17 20:20:05 +08:00
topjohnwu
d5408d1f09 Bump version 2018-03-11 08:28:56 +08:00
topjohnwu
f334532aba Fix strings 2018-03-11 07:33:30 +08:00
dark-basic #DarkBasic BasicHD
be77c09f3d Update Strings
Translation update or translation improvement :D
2018-03-11 07:26:57 +08:00
Jonas Schubert
7de6a92753 added missing update and fingerprint string for german translation 2018-03-11 07:26:33 +08:00
feliph-rc
36f76f5a14 Update strings.xml 2018-03-11 07:26:10 +08:00
Rom
b84523d557 Update French translation
All lines checked 3 times to aoid any problem(s).
2018-03-11 07:26:00 +08:00
topjohnwu
2c78c415e9 Android P cannot install from sdcardfs, use TMPDIR 2018-03-11 05:28:47 +08:00
topjohnwu
79ccb30dd2 Disable SQLite WAL mode manually
Android P seems to default to WAL mode, we don't like it
2018-03-11 04:48:58 +08:00
topjohnwu
3c566becf6 Revert support library
Stupid Google bug: https://issuetracker.google.com/issues/74051124
2018-03-11 04:47:41 +08:00
topjohnwu
151ca593af Update support library 2018-03-04 12:47:15 +08:00
topjohnwu
4132eacba0 Clear folder if installation failed
Close #420
2018-03-03 22:09:12 +08:00
Taras
06e6151816 update Ukrainian translation 2018-03-03 21:00:49 +08:00
Igor Sorocean
70277d4edd update romanian translation 2018-03-03 21:00:37 +08:00
RoySchutte
d21d2f1a9c Update strings.xml 2018-03-03 21:00:17 +08:00
dark-basic #DarkBasic BasicHD
74a7be996f ReUpdate Strings 2018-03-03 21:00:07 +08:00
topjohnwu
3f38579529 Fix strings 2018-02-22 01:29:09 +08:00
topjohnwu
4d5a9f6e15 Bump version 2018-02-22 01:09:55 +08:00
topjohnwu
41f47acd76 Use native XML parser for settings migration 2018-02-22 01:09:55 +08:00
Ilya Kushnir
821dcaa7c7 Update RU strings 2018-02-22 01:09:41 +08:00
vvb2060
7135d26419 Update zh-rCN translation 2018-02-22 01:09:30 +08:00
Oliver Cervera
f7fd354dce Update it strings
- New strings added
2018-02-21 16:58:42 +08:00
dark-basic #DarkBasic BasicHD
0c69a65bc4 Update strings.xml
New Lines added.
New Translation subject to change. :D
2018-02-21 16:58:33 +08:00
Fatih Fırıncı
2f2ca5eab4 Update strings.xml 2018-02-21 16:58:24 +08:00
topjohnwu
df9c40c035 Move to raw resources 2018-02-20 05:07:18 +08:00
topjohnwu
25b67017e4 Update traditional Chinese translation 2018-02-20 03:34:36 +08:00
linar10
bc9c3346f3 Update strings.xml 2018-02-20 03:30:36 +08:00
Vv2233Bb
1db7e19fe8 Updated string-lt 2018-02-20 03:30:23 +08:00
Albert I
102c03ce2b Update Indonesian translations
* Add restore manager strings

Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-02-20 03:29:55 +08:00
Ilya Kushnir
ec19eb4455 Update RU strings 2018-02-20 03:29:39 +08:00
Igor Sorocean
6d9924d50e Update romanian translation 2018-02-20 03:24:56 +08:00
Artem
16c4d74274 Add some Rus translate 2018-02-20 03:24:38 +08:00
Jonas Schubert
e4af5fd36a Added german string values for settings restore 2018-02-20 03:24:19 +08:00
dark-basic #DarkBasic BasicHD
702775493a Update strings.xml
New Line Added.
2018-02-20 03:23:55 +08:00
Oliver Cervera
b2ae826066 Italian - Add option to restore Magisk Manager
- Updated Italian translation with new two strings from 5.6.0
2018-02-20 03:23:44 +08:00
Fatih Fırıncı
cc3e9990fa Update strings.xml 2018-02-20 03:23:30 +08:00
topjohnwu
271cbddd5e Settings improvements 2018-02-20 00:39:17 +08:00
topjohnwu
c1423ca9ad Fix F2FS crashes on SQLite 3.21.0 2018-02-18 18:12:12 +08:00
topjohnwu
74379150a1 Use scripts to setup sudb 2018-02-18 12:41:58 +08:00
topjohnwu
c840a30c30 Bump version 2018-02-13 06:16:24 +08:00
topjohnwu
ae5277a898 Fix multiusers conflicting 2018-02-13 06:05:20 +08:00
topjohnwu
bffa837825 Fix repackaging 2018-02-13 03:27:27 +08:00
topjohnwu
b9e7d0faea Add option to restore Magisk Manager after repackage 2018-02-13 03:22:41 +08:00
topjohnwu
860b08d9ed Add version code to downloaded upgrades 2018-02-13 01:22:43 +08:00
topjohnwu
691dc1d49e Update to libsu 1.1.0 with su I/O 2018-02-12 23:07:35 +08:00
topjohnwu
9d6886d367 Do not allow backups 2018-02-12 03:18:57 +08:00
Taras Korzhak
9589b68f5a Updated UK translation 2018-02-12 03:11:00 +08:00
Albert I
28d88af1af Update Indonesian translations
* Translate new strings
* Improve translation of several strings

Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-02-12 03:10:44 +08:00
Vv2233Bb
8b5acd1849 Update for springs-lt 2018-02-12 03:10:32 +08:00
topjohnwu
33dc63a7fd Fix filenames 2018-02-12 03:09:38 +08:00
topjohnwu
d0a86385b7 Update console messages 2018-02-09 05:38:02 +08:00
topjohnwu
50a49e2c8c Prevent crashes on non rooted devices 2018-02-01 04:42:59 +08:00
topjohnwu
c60adb113e Fix strings 2018-01-31 23:11:31 +08:00
Vv2233Bb
aee015e8f6 Lithuanian translation update 2018-01-31 04:05:03 +08:00
Killer7Mod
bf6af29205 update translation to portuguese-BR 2018-01-31 04:04:48 +08:00
Primokorn
329905d472 Update FR strings.xml 2018-01-31 04:04:36 +08:00
Fatih Fırıncı
00d450d262 Update strings.xml 2018-01-31 04:04:20 +08:00
Jonas Schubert
2365d1bd20 Update german strings 2018-01-31 04:04:04 +08:00
linar10
5b385c18e5 Update strings.xml 2018-01-31 04:03:41 +08:00
Madis
98c0434ec0 Estonian updates 2018-01-31 04:03:23 +08:00
Oliver Cervera
f318d0a3bc Italian - Add fingerprint authentication
Italian translation update
* Add fingerprint authentication
2018-01-31 04:03:03 +08:00
AndroPlus
27f5b410c0 Update Japanese translation 2018-01-31 04:02:48 +08:00
topjohnwu
3f55be9676 Update the method to handle global su db 2018-01-31 04:00:11 +08:00
topjohnwu
b05d2d3a2d Rename module 2018-01-27 08:34:12 +08:00
topjohnwu
19af5f9e0b Remove JNI; use native Java zipadjust 2018-01-27 08:23:02 +08:00
topjohnwu
f37f330670 Update with latest :crypto 2018-01-27 00:17:43 +08:00
topjohnwu
40082d4571 Update to libsu 1.0.0 2018-01-25 18:43:30 +08:00
topjohnwu
00d655f346 Update proguard to minimize APK size 2018-01-23 05:04:59 +08:00
topjohnwu
821726e7c0 Switch to libsu 2018-01-21 06:07:24 +08:00
dark-basic #DarkBasic BasicHD
759e905c3c Update strings.xml
New Lines Added.
2018-01-13 05:58:07 +08:00
topjohnwu
8bf7e42913 Bump version 2018-01-13 05:53:11 +08:00
topjohnwu
0dcd073554 Fix crashes on Lollipop 2018-01-13 05:49:47 +08:00
YumeMichi
2fe35d578d Check fm before using it
* Prevent NPE on devices without fingerprint.
2018-01-13 04:53:19 +08:00
topjohnwu
8d139e156e Adjust proguard settings to prevent crash 2018-01-12 03:33:50 +08:00
topjohnwu
7c2849356a Bump version 2018-01-12 01:57:31 +08:00
topjohnwu
0025ffd1c0 Update Trad. Chinese translation 2018-01-12 01:57:09 +08:00
topjohnwu
2ef7146642 Add fingerprint authentication 2018-01-12 01:53:49 +08:00
Grammatopoulos Apostolos
1b27e69e40 Greek translation updates 2018-01-11 21:04:29 +08:00
topjohnwu
8e7b757efd Fix dtbo detection 2018-01-10 20:41:55 +08:00
Michael Cerne
1ab543cea1 Minor language changes 2018-01-10 19:13:04 +08:00
Vv2233Bb
a3f86903e4 Lithuanian translation 2018-01-10 19:12:30 +08:00
Mevlüt TOPÇU
c239c305ab Update strings.xml 2018-01-10 19:04:26 +08:00
topjohnwu
2e02af994e Bump version 2018-01-02 00:25:08 +08:00
topjohnwu
836d9afe17 Update scripts 2018-01-01 16:46:08 +08:00
topjohnwu
007a352742 Update Trad. Chinese translations 2018-01-01 16:45:50 +08:00
vvb2060
e526e5659e Update zh-rCN translation 2018-01-01 16:39:15 +08:00
Rikka
4a5227c7bf Fix bug in SuDatabaseHelper 2018-01-01 01:11:45 +08:00
AndroPlus-org
c2c151ec4c Update Japanese translation 2018-01-01 01:09:56 +08:00
Jonas Schubert
452096e7e4 Added missing german translations 2018-01-01 01:09:21 +08:00
linar10
50c2a9859e Update strings.xml 2018-01-01 01:09:02 +08:00
Oliver Cervera
677b667307 Add sorting repo by update time
Add translation for new repo strings
2018-01-01 01:08:52 +08:00
topjohnwu
1adf331268 Bump version 2017-12-29 04:03:05 +08:00
topjohnwu
349b3e961b More robust sudb handling 2017-12-29 04:01:39 +08:00
topjohnwu
96650c06f0 Fix the issue that installation configs won't stick 2017-12-29 03:21:51 +08:00
dark-basic #DarkBasic BasicHD
26038a0a07 Update strings.xml 2017-12-29 01:44:36 +08:00
topjohnwu
6a148b5dd9 Add sorting repo by update time 2017-12-27 01:07:33 +08:00
topjohnwu
0e109ef979 Remove snet version checkpoint, always check by code 2017-12-26 18:24:43 +08:00
topjohnwu
de2285d5e9 Bump version 2017-12-26 03:59:28 +08:00
topjohnwu
b2483ba437 Add version check within binary 2017-12-26 03:59:28 +08:00
topjohnwu
a82a5e5a49 Update snet.apk 2017-12-26 03:57:22 +08:00
topjohnwu
d161a02e71 Fix bug in sudb init 2017-12-25 01:38:38 +08:00
Ilya Kushnir
d2b6a700b1 Update RU strings 2017-12-25 01:37:05 +08:00
Matthias Urhahn
af203cef24 Update strings.xml
Improved german translation.
2017-12-25 01:36:52 +08:00
Madis
673e917e76 et: Missing strings and improvements 2017-12-25 01:36:38 +08:00
RoySchutte
a3bd41db54 Update strings.xml 2017-12-25 01:36:20 +08:00
topjohnwu
0d9527921a Fix su time limits 2017-12-22 06:43:55 +08:00
topjohnwu
f0e4aec0af Bump version 2017-12-22 02:36:26 +08:00
topjohnwu
b0d65b5edd Improve compatibility 2017-12-22 02:36:26 +08:00
topjohnwu
75532ef591 Add recommended KEEPVERITY and KEEPFORCEENCRYPT flags 2017-12-22 02:36:20 +08:00
topjohnwu
9a6d1bd700 Add self package into blacklist 2017-12-22 02:36:20 +08:00
topjohnwu
a7ed6c15d3 More precise sudb management 2017-12-22 02:36:15 +08:00
vvb2060
5ee49ba065 Update zh-rCN translation 2017-12-22 00:40:38 +08:00
topjohnwu
d34bd47bea Read full css into memory for MarkdownWindow 2017-12-20 00:40:19 +08:00
topjohnwu
f17792380b Update Trad. Chinese translation 2017-12-19 23:07:33 +08:00
topjohnwu
c11920110e Update German Translation
Credit: @GuepardoApps
2017-12-19 23:03:09 +08:00
Oliver Cervera
ec5a993fea Update Italian strings
* 2 new strings have been added
2017-12-19 23:01:17 +08:00
linar10
d250c2cc89 Update strings.xml 2017-12-19 23:01:01 +08:00
Grammatopoulos Apostolos
767e73f40c Greek translation updates 2017-12-19 23:00:44 +08:00
Small_Ku
3f699c9d2f Fix a minor translation mistake 2017-12-19 22:59:54 +08:00
dark-basic #DarkBasic BasicHD
50dbd9befd Update Strings.xml 2017-12-19 22:59:16 +08:00
Matthias Sweertvaegher
760e01bf92 request focus for grant button to enable dpad nav
if no buttons have focus, it is impossible to use
on android tv without hooking up a mouse
2017-12-19 22:56:10 +08:00
topjohnwu
543f435b1e Massive improvement of Magisk Manager repackaging 2017-12-19 20:59:59 +08:00
topjohnwu
91337218b3 Update snet configs 2017-12-19 15:46:54 +08:00
topjohnwu
afff3c0a49 Update snet.apk 2017-12-19 15:44:39 +08:00
topjohnwu
a1871e4bc3 Fix install commands 2017-12-18 03:02:19 +08:00
topjohnwu
3aa0294cd4 Fix strings.xml 2017-12-16 23:15:01 +08:00
topjohnwu
310b266251 Fix installation on FBE devices 2017-12-16 04:31:31 +08:00
Grammatopoulos Apostolos
21b1b5098e Greek translation update and fixes 2017-12-16 03:38:41 +08:00
dark-basic #DarkBasic BasicHD
a3a4a5d8a5 Update Strings.xml
It has been compared with strings.xml in English and I have updated based on the new restructuring of the project
2017-12-16 03:38:29 +08:00
Oliver Cervera
270536f33c Update Italian strings
- Now based on new project restructure
- New strings have been added and translated
- Some strings have been revised and updated based on feedback
2017-12-16 03:37:21 +08:00
linar10
66bb433cc6 Update strings.xml 2017-12-16 03:36:58 +08:00
Fatih Fırıncı
bd4ef1a03a Update strings.xml 2017-12-16 03:36:42 +08:00
topjohnwu
aa2d9a3bf1 Support installing to new path 2017-12-16 02:01:04 +08:00
topjohnwu
fd6cbb138c Change new magisk database 2017-12-12 02:35:00 +08:00
topjohnwu
aa75c8e5e4 Fix issues of repackaging with multiuser 2017-12-08 23:38:03 +08:00
topjohnwu
c461fc6daa Adapt with new Magisk installation 2017-12-07 04:20:15 +08:00
topjohnwu
96eaa833f5 Update README.md 2017-12-04 22:59:06 +08:00
topjohnwu
863b13a694 Massive project restructure 2017-12-04 14:21:55 +08:00
Igor Sorocean
e6fea4e6dd Update romanian translation 2017-12-04 13:45:47 +08:00
vvb2060
83bfc13056 Update zh-rCN translation 2017-12-04 13:45:18 +08:00
dark-basic #DarkBasic BasicHD
bc4f09209b Update strings.xml
New Lines Added. --> Add reboot menu
Updated Translations --> Add Changelogs
New Line Added ---> Cleanup prefs
2017-12-04 13:45:05 +08:00
topjohnwu
967ca17238 Fix custom channel dialog 2017-12-03 15:43:07 +08:00
topjohnwu
595c72147c Add dark theme to superuser request 2017-12-03 15:15:00 +08:00
topjohnwu
f3c3b5a649 Cleanup prefs 2017-12-03 04:18:22 +08:00
topjohnwu
1cd2c5e653 Add changelogs 2017-12-03 04:18:22 +08:00
topjohnwu
b2873dd44b Add reboot menu 2017-12-02 22:50:59 +08:00
topjohnwu
bb80ab4026 Support migrating settings after repackage 2017-12-02 02:35:07 +08:00
topjohnwu
80cabb338b Java has native inputstream wrapper 2017-12-01 11:42:05 +08:00
topjohnwu
2c69e2c151 Update SignAPK to use less memory 2017-12-01 11:19:38 +08:00
linar10
c1dd23f5e0 Update strings.xml 2017-11-30 00:08:14 +08:00
Jonas Schubert
f93624a41c updated german translation 2017-11-30 00:08:04 +08:00
Albert I
9f4559a059 Initial Indonesian translations
This brings Indonesian language support to Magisk Manager.

Signed-off-by: Albert I <krascgq@outlook.co.id>
2017-11-30 00:07:52 +08:00
Igor Sorocean
fd05cad303 Update romanian translation 2017-11-30 00:07:37 +08:00
Madis
d58b06e493 Estonian update
New strings and better wording
2017-11-30 00:07:22 +08:00
Mevlüt TOPÇU
2f0b549027 Update strings.xml 2017-11-25 00:31:58 +08:00
Ilya Kushnir
87dbd7e541 Update RU strings 2017-11-25 00:31:50 +08:00
topjohnwu
96e5da36be Update snet.apk link 2017-11-24 22:25:42 +08:00
topjohnwu
43745edac0 Fix crashes when Google Play Service require update 2017-11-24 22:15:46 +08:00
topjohnwu
f5ceee547c Bump version 2017-11-23 23:34:46 +08:00
topjohnwu
b612bce779 Add FLAG_ACTIVITY_NEW_TASK flag for updates 2017-11-23 23:26:06 +08:00
topjohnwu
2e88e5e9c7 Fix strings 2017-11-23 23:19:31 +08:00
Primokorn
9a7aa25c90 Update FR strings.xml 2017-11-23 23:18:13 +08:00
uvera
c4420fe932 Create values-sr
Serbian translation
2017-11-23 23:18:04 +08:00
Oliver Cervera
a5260f3a95 Update Italian strings 2017-11-23 23:17:47 +08:00
topjohnwu
47ccf4b1f5 Bump version 2017-11-23 01:06:19 +08:00
topjohnwu
a356b21895 Prevent hiding Magisk Manager on old Magisk versions 2017-11-23 01:06:18 +08:00
dark-basic #DarkBasic BasicHD
614a36c888 Update strings.xml
New Lines added.
2017-11-23 00:12:23 +08:00
topjohnwu
f520fe36bd Update to use new paths 2017-11-22 14:03:15 +08:00
vvb2060
7273a1c34d Update zh-rCN translation 2017-11-21 21:49:40 +08:00
Oliver Cervera
dc45cbce37 Update Italians strings
All new strings translated + clean-up!
2017-11-21 21:49:28 +08:00
topjohnwu
708d8f75c0 Notify su db corruption 2017-11-21 02:21:37 +08:00
dark-basic #DarkBasic BasicHD
bd37d90228 Update strings.xml 2017-11-21 02:15:14 +08:00
topjohnwu
b1ad691464 Several small fixes 2017-11-21 02:15:13 +08:00
topjohnwu
f4e7baf31e Update snet.apk link 2017-11-21 00:43:13 +08:00
topjohnwu
c0e60c41f2 Update snet extension pack 2017-11-21 00:40:05 +08:00
topjohnwu
c8dad43e00 Fix boot patching 2017-11-21 00:34:25 +08:00
topjohnwu
a8f124704d Allow custom update channels 2017-11-20 03:09:08 +08:00
vvb2060
eed2816491 Update zh-rCN translation 2017-11-19 22:48:29 +08:00
linar10
a6334b3e35 Update strings.xml 2017-11-19 22:48:20 +08:00
topjohnwu
334beebfeb Not all devices work well with streaming 2017-11-19 06:17:31 +08:00
topjohnwu
13dad848bd Fix download progress bug for modules larger than 20MB 2017-11-18 14:17:26 +08:00
topjohnwu
e518f4cef8 Crash proof database: reset if error occurs 2017-11-18 05:17:06 +08:00
topjohnwu
c8fd5da2da Remove unused strings 2017-11-18 05:17:06 +08:00
topjohnwu
3a74729ecc Add saving logs feature for installation 2017-11-18 05:17:06 +08:00
topjohnwu
49c672ac4d Add STDERR support 2017-11-18 05:17:06 +08:00
topjohnwu
b570cb5b77 Extract external path 2017-11-18 05:17:06 +08:00
topjohnwu
97bf388471 Support new module specification 2017-11-18 05:17:05 +08:00
topjohnwu
1a32aaea6f Drawer rearrangement 2017-11-18 05:17:05 +08:00
topjohnwu
4635883dec Update to use adaptive icons 2017-11-18 03:56:34 +08:00
topjohnwu
3ba6db4a50 Update Trad. Chinese translation 2017-11-17 02:28:51 +08:00
Xorok
2f1de25747 Fix color of LogFragment menu items when using dark theme
I set the color directly in the ic_*.xml files instead of using android:iconTint in menu_log.xml (as seen in fragment_magisk.xml) because iconTint is API26+.
2017-11-17 02:14:13 +08:00
daveyannihilation
f60fd42ac0 Expose Flashing colours for themes 2017-11-17 02:03:35 +08:00
RoySchutte
ecc8f9c792 Update strings.xml 2017-11-17 01:45:05 +08:00
dark-basic #DarkBasic BasicHD
e295dfdcf7 Update strings.xml 2017-11-17 01:44:56 +08:00
Oliver Cervera
fc42c25390 Update IT translation for new strings
Updating Italian translation for new strings that have just been pushed.
2017-11-17 01:44:47 +08:00
topjohnwu
27d5858e06 Fix file selection for module install 2017-11-17 01:39:34 +08:00
Generator
e1ef732b60 update pt_PT translation 2017-11-15 05:44:13 +08:00
RoySchutte
9840b95c21 Update strings.xml 2017-11-15 05:44:05 +08:00
linar10
a6f8446d81 Update strings.xml 2017-11-15 05:43:56 +08:00
Oliver Cervera
c1c844c830 Update strings Italian
Urgent correction!
Many strings contain the following character
'
It needs a backslash \ typed in front, otherwise sentences are cut!
2017-11-15 05:43:46 +08:00
topjohnwu
389299afd1 Remove apps from hidelist if uninstalled 2017-11-15 05:36:57 +08:00
topjohnwu
826543a291 Fully support dtbo.img patching 2017-11-15 05:36:57 +08:00
topjohnwu
4ac83cfded Small UI improvement 2017-11-15 00:38:38 +08:00
topjohnwu
64c363ce53 Update repo download progress report 2017-11-09 02:12:55 +08:00
topjohnwu
cca4347bf9 Use handler instead of weird callbacks 2017-11-09 01:43:29 +08:00
topjohnwu
3ae3d4926a Small adjustments to UI 2017-11-09 01:11:50 +08:00
topjohnwu
36025d6d9f Use direct path 2017-11-09 00:03:37 +08:00
topjohnwu
e171362e3e Improve snet.apk downloading 2017-11-07 00:39:48 +08:00
topjohnwu
3e0bf2ae15 Bump version 2017-11-06 23:21:05 +08:00
dark-basic #DarkBasic BasicHD
07aa9f4b8b Update strings.xml
new lines added
2017-11-06 23:04:59 +08:00
Oliver Cervera
b2d9f3fc64 Update Italian IT strings 2017-11-06 23:04:46 +08:00
Taras Korzhak
5fb3e9167e Updated Ukrainian translation 2017-11-06 23:04:28 +08:00
topjohnwu
99c74b31be Improve dynamic permissions 2017-11-06 05:40:41 +08:00
topjohnwu
ce5b13824e Organize application initialization 2017-11-06 04:47:24 +08:00
topjohnwu
c39170c42e Organize constants 2017-11-06 04:41:23 +08:00
topjohnwu
fd19fbf300 Improve Magisk direct install 2017-11-04 04:01:58 +08:00
topjohnwu
166469827f Support new sha1 location 2017-11-03 05:02:14 +08:00
topjohnwu
a34ed538b6 Fix potential bug 2017-11-03 02:25:42 +08:00
topjohnwu
5f22d3e055 Support new xml binary format 2017-10-31 22:48:48 +08:00
topjohnwu
fdd700f3e5 Update boot signing in InstallMagisk 2017-10-31 16:31:58 +08:00
topjohnwu
adf930f126 Finalize bootsigner commandline 2017-10-31 02:55:50 +08:00
topjohnwu
05f41928cd Add boot signing 2017-10-30 03:45:22 +08:00
topjohnwu
2ee0829871 Fix strings.xml 2017-10-30 03:44:03 +08:00
Dmitry Val'd
743560825d Update RU translation
Added new lines from original + corrected mistakes of the previous version of translation
2017-10-29 19:05:22 +08:00
Antoine
e3d84ac349 Update french translation 2017-10-29 19:05:12 +08:00
Dino Dugandžija
266c832b30 Created Croatian translation
I've translated the Magisk Manager app strings.xml to Croatian language. If anything else is needed, please let me know.
2017-10-29 19:04:55 +08:00
topjohnwu
f5374a024e Improve dynamic loading snet package 2017-10-29 14:43:43 +08:00
topjohnwu
4956d826fb Fix UID stored in multiuser mode 2017-10-28 16:19:53 +08:00
topjohnwu
f5cc2af5d0 Repackage Magisk Manager for hiding 2017-10-28 16:19:53 +08:00
topjohnwu
5880d4a6ec Use global su database 2017-10-28 15:50:17 +08:00
topjohnwu
ae05dce958 Improve Shell and logging 2017-10-21 02:28:44 +08:00
topjohnwu
9ebe372a9a Simplify flash log screen 2017-10-21 02:28:44 +08:00
topjohnwu
e6e04cc5b3 Add reference ASAP 2017-10-16 11:51:34 +08:00
topjohnwu
12352510fd Fix strings 2017-10-16 11:47:07 +08:00
vvb2060
2b3d927937 Update zh-rCN translation 2017-10-16 11:11:27 +08:00
Madis
a8890740f5 Created Estonian translation
I translated Magisk Manager to Estonian with the help of an app called Stringlate.
2017-10-16 11:11:19 +08:00
dark-basic #DarkBasic BasicHD
f60d7ee54b Fix Strings.xml
Translation Mistakes corrected.
2017-10-16 11:11:10 +08:00
topjohnwu
896ca2ef6b Cleanup contexts 2017-10-16 00:54:48 +08:00
topjohnwu
c036f6d529 Cleanup Utils 2017-10-15 23:54:34 +08:00
topjohnwu
6f457c0c59 Refactor shell (again) 2017-10-15 23:02:44 +08:00
Dmitry Val'd
13bf1b27b4 Update strings.xml
Added new lines from original
2017-10-15 03:15:39 +08:00
topjohnwu
f742bb1c47 Hot fix for detecting MagiskHide 2017-10-15 03:12:13 +08:00
topjohnwu
aa0b9e2db2 Bump version 2017-10-14 04:18:14 +08:00
topjohnwu
c10076f7ed Remove debug logs 2017-10-14 04:05:41 +08:00
topjohnwu
bcd92499f2 Massive improvement on Online Repo fetching 2017-10-14 04:05:41 +08:00
topjohnwu
b2bb0d4f72 Fix some external storage permission issues 2017-10-14 00:36:10 +08:00
topjohnwu
e140481f14 Wrap wrapper with buffer 2017-10-13 20:47:14 +08:00
topjohnwu
186bd11463 Reconnect until we got content length 2017-10-13 03:25:56 +08:00
topjohnwu
a0490d6687 Update Trad. Chinese translation 2017-10-13 03:10:35 +08:00
killer7mod
beef740ade update strings.xml for PT-BR 2017-10-13 02:45:02 +08:00
Frieder Bluemle
2ac7786a90 Update commonmark to 0.10.0 2017-10-13 02:44:42 +08:00
Frieder Bluemle
a3fb5e910f Update bouncycastle libs to 1.58 2017-10-13 02:44:42 +08:00
Frieder Bluemle
319afe86b5 Update Gradle wrapper to 4.2.1 2017-10-13 02:44:42 +08:00
Frieder Bluemle
762ab66b86 Fix Lint errors 2017-10-13 02:44:42 +08:00
topjohnwu
0c239a42de Allow secondary users to control Superuser settings except multiuser options 2017-10-13 02:41:43 +08:00
dark-basic #DarkBasic BasicHD
e9322fba26 Update strings.xml
New Lines Added
2017-10-07 23:44:10 +08:00
RoySchutte
39b6df27b3 Update strings.xml 2017-10-07 20:55:00 +08:00
topjohnwu
b1ee284e7f Rename resource -> common 2017-10-07 20:48:45 +08:00
topjohnwu
e986332bf2 Several small snet fixes 2017-10-07 20:47:44 +08:00
topjohnwu
48f9b27381 Seperate JarSigner and add task for host 2017-10-07 20:31:49 +08:00
topjohnwu
42a6e0dd10 Seperate Google proprietary code 2017-10-07 17:12:36 +08:00
topjohnwu
d4798b02ac Move functions 2017-10-04 22:27:14 +08:00
topjohnwu
963edfe8ab Add InputStream mode for signing zips 2017-10-04 22:09:59 +08:00
topjohnwu
53237f3ae0 Update Android Studio and Proguard configs 2017-10-04 15:23:08 +08:00
topjohnwu
64da9281a4 Show progress while downloading modules 2017-10-01 02:38:25 +08:00
topjohnwu
ab7fd9799d Remove cache module exception 2017-10-01 01:38:25 +08:00
topjohnwu
f6bcc84251 Improve repo fetching 2017-10-01 01:28:50 +08:00
topjohnwu
35dc3d9df9 Update WebService 2017-10-01 01:12:45 +08:00
topjohnwu
566714a75d Use override functions 2017-09-30 03:25:50 +08:00
topjohnwu
c92f30b122 Re-organize classes 2017-09-30 03:04:23 +08:00
topjohnwu
294ad094c4 Show repo loading progress by showing repos already loaded 2017-09-30 01:15:34 +08:00
topjohnwu
c1a0f520f9 Prevent flash screen close when tapping outside 2017-09-29 13:20:34 +08:00
topjohnwu
773c24b7fc Bump version 2017-09-28 03:55:53 +08:00
topjohnwu
8f926c7ca9 Load scripts in memory 2017-09-28 03:33:56 +08:00
topjohnwu
c562cbc2bb Update zip and magisk installation 2017-09-26 20:46:58 +08:00
topjohnwu
3fbbb0865a Update trad. Chinese 2017-09-26 02:13:39 +08:00
Naboleo
7d5f612a48 Update strings.xml 2017-09-26 03:07:55 +09:00
linar10
4a5a36440b Update strings.xml 2017-09-26 03:07:41 +09:00
Dmitry Val'd
43dd5cfea1 Update RU translation
Added new or missing lines
2017-09-26 03:07:33 +09:00
dark-basic #DarkBasic BasicHD
7b5fec1842 Update strings.xml 2017-09-26 03:07:20 +09:00
topjohnwu
5762ded601 Properly detect hosts file 2017-09-25 17:55:40 +08:00
topjohnwu
a3abb86daa Only place files in de on FDE enabled devices 2017-09-24 21:29:01 +08:00
topjohnwu
4f5c656b05 Update uninstall method 2017-09-16 03:53:13 +08:00
topjohnwu
a31cddbe7b Prevent NPE 2017-09-16 02:41:24 +08:00
topjohnwu
b4ecd93f1c Proper FBE support: place files in DE 2017-09-15 18:03:25 +08:00
topjohnwu
0acc23e058 Allow dialog to popup 2017-09-15 13:55:36 +08:00
topjohnwu
cdd5f9b628 Fix busybox installation 2017-09-15 13:34:53 +08:00
topjohnwu
4c9f5f4655 Support patching second slot 2017-09-15 13:03:10 +08:00
topjohnwu
b80ba13cb4 Fix strings 2017-09-15 03:47:18 +08:00
Santiago Pintos
8260bdc09c Update translations into spanish
Add two strings: "zip_download_title" and "zip_download_msg"
2017-09-13 10:12:13 -05:00
RoySchutte
24f856e02b Update strings.xml 2017-09-13 10:12:03 -05:00
Mevlüt TOPÇU
3aa619b928 Update
Merge please

Thank you
2017-09-13 10:11:53 -05:00
Taras Korzhak
4cb5e98d94 Update Ukrainian translation 2017-09-13 10:11:25 -05:00
Primokorn
272910575e Update FR strings.xml
Stupid typo
Unhide Magisk Manager should not be translated
2017-09-13 10:09:37 -05:00
topjohnwu
a15a62f4bc Move logic to external script file 2017-09-13 23:07:59 +08:00
topjohnwu
53cf11db8c Fix failure if MagiskManager folder doesn't exist 2017-09-13 23:07:59 +08:00
Dmitry Val'd
01052fbe47 Update strings.xml 2017-09-07 10:45:27 +08:00
dark-basic #DarkBasic BasicHD
a5e1e075c7 Update Strings (6-9-17)
Small Update
New Line Added.
2017-09-07 10:45:12 +08:00
c727
6be32ac688 update german strings
small improvements for new strings
also unified some strings

@topjohnwu:
what do you thing about calling the hidden Magsik Manager also "Magisk Manager" instead of "Unhide Magisk Manager"
The hidden status could be symbolized by an incognito style version of the app icon
advantages:
-same position in app drawer
-no need to translate it
2017-09-07 10:45:02 +08:00
topjohnwu
b362c0ef38 Bump version 2017-09-06 23:06:18 +08:00
topjohnwu
bba9969e31 Fix install button hiding 2017-09-06 23:05:51 +08:00
Primokorn
007ba24809 Update FR strings.xml 2017-09-06 22:33:04 +08:00
topjohnwu
df21539311 Some versioning fixes 2017-09-06 22:32:40 +08:00
topjohnwu
2592cb6019 Show Install button after update check done 2017-09-06 16:28:24 +08:00
topjohnwu
f7df17a7ed Small fix 2017-09-06 15:42:45 +08:00
dark-basic #DarkBasic BasicHD
62f42b72f8 Update Strings.xml (05-09-17)
New lines added.
2017-09-06 14:42:22 +08:00
topjohnwu
a1ba4fda6f Improve install Magisk 2017-09-06 14:41:59 +08:00
topjohnwu
1c06b04c45 Use GNU tar format 2017-09-06 13:39:29 +08:00
topjohnwu
2ee22fd374 Add restore stock image feature 2017-09-05 17:43:13 +08:00
topjohnwu
4c230d9e61 Root shell workaround 2017-09-05 13:46:54 +08:00
topjohnwu
727294fbbe Disable D8, dex not compatible with Android 5.0 2017-09-05 02:57:30 +08:00
Dmitry Val'd
478c43969b Update strings.xml
Added missing/new lines
2017-09-05 02:50:36 +08:00
Jens Lody
79b5303350 Update german translation 2017-09-05 02:50:20 +08:00
topjohnwu
ce4b742b25 Support .img.tar as input 2017-09-04 01:57:45 +08:00
topjohnwu
a9dc15bda5 Update TW translations 2017-09-04 01:14:38 +08:00
topjohnwu
ba6387ff5c Resource cleanup! 2017-09-04 00:58:39 +08:00
linar10
8fa98508b7 Update strings.xml 2017-09-03 23:18:12 +08:00
Dmitry Val'd
decdbaecf9 Update strings.xml
Added missing lines
2017-09-03 23:18:02 +08:00
gh2923
6d87cf9be0 Update Simplified Chinese Translation 2017-09-03 23:17:53 +08:00
Leonidas P
94f434c4a6 Translate Update Channel Strings 2017-09-03 23:17:36 +08:00
dark-basic #DarkBasic BasicHD
7ba867c30b Update Strings - (New Update 03-09-17) 2017-09-03 23:17:18 +08:00
topjohnwu
3424395e10 Calculate offset for unhide 2017-09-03 23:00:54 +08:00
topjohnwu
926c7359a2 Merge download and process repo modules 2017-09-03 22:10:54 +08:00
topjohnwu
ec0af99a2e Fix locale settings 2017-09-03 21:12:09 +08:00
topjohnwu
b4d948886c Fix unzip issues 2017-09-03 21:05:57 +08:00
topjohnwu
4d8d79372a Update strings 2017-09-03 18:28:46 +08:00
topjohnwu
04a589722c Support .img.tar format for ODIN 2017-09-03 17:46:00 +08:00
topjohnwu
d4a10e2873 Various adjustments 2017-09-03 17:46:00 +08:00
topjohnwu
4998ad6c7e Show Manager updates in dialogs 2017-09-03 14:58:21 +08:00
topjohnwu
a07ca5ff50 Slightly change busybox handling 2017-09-03 03:26:01 +08:00
topjohnwu
f07e7571ab Change block detection method 2017-09-03 02:45:43 +08:00
topjohnwu
834c16485c Reduce unnecessary code 2017-09-03 02:34:23 +08:00
topjohnwu
04a4265ef3 Show correct message 2017-09-03 00:17:42 +08:00
topjohnwu
0ec473195d Update install Magisk method 2017-09-03 00:10:14 +08:00
topjohnwu
0bf09256b0 Update Android Studio and Gradle 2017-09-02 19:12:03 +08:00
topjohnwu
db8fd2c913 Add boot image file patch 2017-08-31 03:07:33 +08:00
topjohnwu
dbe6e5b3d7 Simplify app startup 2017-08-30 02:28:24 +08:00
topjohnwu
cc81cd446b Extract ExpandableView code into interface 2017-08-29 04:10:04 +08:00
topjohnwu
439c7118f1 Proper runtime permission implementation 2017-08-29 03:08:09 +08:00
topjohnwu
d8154a5815 Update deprecate code 2017-08-29 01:56:43 +08:00
topjohnwu
4e3787bc0d Add beta update channel 2017-08-29 01:34:42 +08:00
topjohnwu
02e0955924 Fix settings crash 2017-08-29 00:37:52 +08:00
topjohnwu
a78950e822 Reduce boilerplate 2017-08-28 00:27:10 +08:00
topjohnwu
1ce1a94a35 Update translations 2017-08-27 01:38:05 +08:00
gh2923
977b6d9f67 Update Simplified Chinese Translation 2017-08-27 01:09:49 +08:00
Igor Sorocean
b5e6dbd797 update romanian translation 2017-08-27 01:09:41 +08:00
Taras
833e6688f1 Added Ukrainian translation 2017-08-27 01:09:33 +08:00
Dmitry Val'd
bc22c9f84f Update strings.xml
Added missing strings
2017-08-27 01:08:25 +08:00
Mevlüt TOPÇU
2149a7d116 Update
Merge please
2017-08-27 01:08:14 +08:00
dark-basic #DarkBasic BasicHD
29175d2c17 Update Strings.xml 2017-08-27 01:07:45 +08:00
Leonidas P
803454d5c8 Update Greek Strings 2017-08-27 01:07:26 +08:00
topjohnwu
36cf32dc42 Change unhide app temp location 2017-08-27 01:04:55 +08:00
topjohnwu
657f4ab303 Add hide Magisk Manager feature 2017-08-22 03:01:54 +08:00
topjohnwu
ea6552615d Bump version 2017-08-13 01:50:20 +08:00
Generator
4bf3287fce update pt_PT 2017-08-13 01:20:04 +08:00
Mevlüt TOPÇU
832c2034c2 Update
Hi,

Update, translations and typo fix

Merge please

Thank you
2017-08-13 01:19:48 +08:00
RJ Trujillo
b0aa26e1f1 More string updates
* A few grammatical corrections were made
* Everything looks cleaner now
2017-08-13 01:19:27 +08:00
dark-basic #DarkBasic BasicHD
e52baeb967 Update Strings.xml 2017-08-13 01:19:15 +08:00
Leonidas P
8268eb9a83 Update strings.xml 2017-08-13 01:18:55 +08:00
topjohnwu
3cc458abd9 Always use global mount namespace 2017-08-12 17:07:28 +08:00
topjohnwu
337b4c4268 Upgrade Android Studio 2017-08-12 15:54:14 +08:00
topjohnwu
001f8657f6 Use global Magisk native busybox for Magisk Manager 2017-08-12 02:25:55 +08:00
topjohnwu
ea884e7fa1 Re-organize application startup 2017-08-12 01:31:34 +08:00
topjohnwu
1b1394cf5d Improve Markdown support
Close #259
2017-08-08 16:12:49 +08:00
topjohnwu
1eef930dbb Move OnClickListener to Butterknife 2017-08-08 16:09:45 +08:00
topjohnwu
1e175e74ed Prevent crashes 2017-08-07 00:15:46 +08:00
John Wu
75a46c365e Update README.md 2017-08-04 00:23:14 +08:00
topjohnwu
8e7b8825f5 Rename callbackevents to topic/subscribers 2017-08-04 00:17:31 +08:00
topjohnwu
2ecbca303b Update Shell 2017-08-03 23:33:08 +08:00
topjohnwu
8195a4d616 Don't ignore libbusybox.so, we want it removed 2017-08-01 23:54:45 +08:00
topjohnwu
7ba40f925f Remove busybox in APK, download from internet 2017-08-01 23:52:39 +08:00
topjohnwu
345cd1795f Update WebService 2017-08-01 23:08:34 +08:00
topjohnwu
959aaee045 Fix FlashZip crash when fails 2017-07-31 01:19:43 +08:00
topjohnwu
53477f0f59 Improve locale settings 2017-07-31 00:44:38 +08:00
topjohnwu
5716218f41 Update busybox version and bug fixes 2017-07-31 00:21:18 +08:00
topjohnwu
9df6b9d5c0 Remove external files from git
These files should be copied to the correct place by Magisk's build script
2017-07-30 23:17:39 +08:00
topjohnwu
ec46031d36 Update Android Studio 2017-07-30 14:41:22 +08:00
RJ Trujillo
55b84d166a Improve dialog strings
* A space should never follow a question mark or any form of punctuation
* Multiple exclamation marks are not needed
2017-07-30 01:36:25 -05:00
Silvered99
34ae8bacec Update strings.xml 2017-07-30 01:36:16 -05:00
RoySchutte
cb4e5ca0f7 Update strings.xml 2017-07-30 01:36:07 -05:00
Leonidas P
0ba45468c4 Fix typos
these pesky little buggers, you never find them...
2017-07-30 01:35:57 -05:00
Frieder Bluemle
710502784e Update Gradle wrapper to 4.1-rc-1 2017-07-30 01:35:46 -05:00
topjohnwu
0275a8558d Fix locale settings duplicate 2017-07-24 18:37:13 +08:00
topjohnwu
58acc75cf6 Fix SuLog UI 2017-07-24 13:15:05 +08:00
topjohnwu
874ababb9f Fix strings.xml 2017-07-24 02:08:58 +08:00
gh2923
3771e6b0cd Update Simplified Chinese Translation 2017-07-24 01:38:55 +08:00
Sopor
33eaefa966 Add Swedish translation 2017-07-24 01:38:43 +08:00
RoySchutte
cd7e236d57 Update strings.xml 2017-07-24 01:38:18 +08:00
Andrei Conache
54c0b7c7d5 update italian translation 2017-07-24 01:38:02 +08:00
zertyuiop
a2177daec2 Update strings.xml 2017-07-24 01:37:42 +08:00
dark-basic #DarkBasic BasicHD
628386b453 Update Spanish strings.xml 2017-07-24 01:37:23 +08:00
Leonidas P
b222bfb3e0 Update Greek translation 2017-07-24 01:36:09 +08:00
topjohnwu
ab199d883d Change su logs time granularity 2017-07-24 01:26:56 +08:00
topjohnwu
356065d1ee Rewrite SuLogAdapter 2017-07-24 01:26:56 +08:00
topjohnwu
76e7c5623d Simplify ApplicationAdapter filter 2017-07-24 01:26:56 +08:00
topjohnwu
085fba050a Introduce self-written SectionedAdapter 2017-07-24 01:26:45 +08:00
topjohnwu
295334d3ac Preserve toolbar elevation when restart activity 2017-07-23 00:47:54 +08:00
topjohnwu
36124ddca4 Update CallbackEvents 2017-07-23 00:39:38 +08:00
topjohnwu
bd6585765e Add locale settings 2017-07-23 00:33:24 +08:00
topjohnwu
c325deb4ed Random changes 2017-07-22 17:39:34 +08:00
topjohnwu
73bb0b10ee Prevent memory leak in CallbackEvent 2017-07-21 05:18:24 +08:00
topjohnwu
72820b162c Code cleanups 2017-07-21 05:08:39 +08:00
topjohnwu
89e5b8d057 Switch to official BouncyCastle 2017-07-21 03:56:48 +08:00
topjohnwu
da4f53ebbb Don't store multiple repo copies in memory 2017-07-21 02:46:19 +08:00
topjohnwu
8458553b74 Update database helper 2017-07-21 02:10:00 +08:00
topjohnwu
55ecc41d06 Bump version 2017-07-20 03:20:17 +08:00
#DarkBasic - BasicHD
28fcdf2cbb Update strings.xml
Delele Translate "Magisk Modo Sólo Núcleo". (After several hours (Days :v ). I thought it was best left in its original form .Magisk Hide, should also be translated if it were the case, it was better to leave it that way so as not to confuse the users.)
Fix translation error
Translations Updates and added new line
2017-07-20 03:19:58 +08:00
topjohnwu
24087679a8 Update uninstaller 2017-07-20 02:56:36 +08:00
topjohnwu
5ac6a8cb4a Small minor updates 2017-07-20 02:54:34 +08:00
topjohnwu
668d85d14e Improve notification support 2017-07-20 01:44:32 +08:00
topjohnwu
c11a3dc95c Fix Magisk Manager freezing issue 2017-07-20 00:51:30 +08:00
topjohnwu
56f57c20a2 Update AsyncTasks to prevent memory leak 2017-07-19 18:01:22 +08:00
topjohnwu
240d14779a Minor cleanup in check updates 2017-07-19 16:10:17 +08:00
topjohnwu
3550d1e61c Bump version 2017-07-19 00:38:25 +08:00
topjohnwu
6513ad249c Fix string.xml 2017-07-19 00:36:54 +08:00
killer7mod
50297b1880 update strings.xml portuguese brazil 2017-07-19 00:25:36 +08:00
#DarkBasic - BasicHD
f189b78b9e #DBC01 - Translation update 2017-07-19 00:25:23 +08:00
zertyuiop
5c0250f495 Fix too long string
checking_safetyNet_status string is too long.
2017-07-19 00:24:47 +08:00
pavlaras
2093f726e9 Update strings.xml
corrected Greek translation
2017-07-19 00:24:36 +08:00
topjohnwu
10efe3859d Update repo fragment and adapter 2017-07-18 23:18:57 +08:00
topjohnwu
6933bcf7bb Merge shells 2017-07-18 17:14:42 +08:00
topjohnwu
2ea046cd80 Add flashing screen 2017-07-18 17:14:42 +08:00
topjohnwu
f4097a372b Root shell with no outputs 2017-07-18 01:06:05 +08:00
topjohnwu
87ea2a2bef Rewrite root shell 2017-07-16 03:00:01 +08:00
JpegXguy
cc14a1c361 Fix untranslated strings 2017-07-15 01:23:59 +08:00
topjohnwu
bcdface60d Fix crashing when installing modules 2017-07-15 01:22:00 +08:00
topjohnwu
4dc9419d2e Bump version 2017-07-14 02:31:29 +08:00
topjohnwu
d2bcac813e Fix update notifications on Android O 2017-07-14 02:27:02 +08:00
topjohnwu
080c37a7f6 Remove busybox from strings 2017-07-14 01:18:20 +08:00
topjohnwu
f9a3838db6 Fix strings 2017-07-13 15:37:00 +08:00
JpegXguy
1e61db104b Added Greek Language 2017-07-13 15:22:53 +08:00
Generator
30a9c7718d Added (European) Portuguese
Split Portuguese into pt_BR and pt_PT
2017-07-13 15:22:40 +08:00
Dmitry Val'd
34b052b5d3 Update strings.xml
Full and correct translation to russian language
2017-07-13 15:21:27 +08:00
topjohnwu
aaa12853ad Prevent crashing when requesting SN check while checking
Fixed #208, fixed #212
2017-07-13 15:12:43 +08:00
topjohnwu
b0ab55b0bf Only show one notification at a time 2017-07-13 14:51:12 +08:00
topjohnwu
d2f8496f4e Update dependency 2017-07-13 14:47:47 +08:00
topjohnwu
1a69b16d36 Bump version 2017-07-11 01:11:10 +08:00
topjohnwu
b5e8673e62 Fix small UI bug 2017-07-11 01:09:40 +08:00
topjohnwu
264c6a50b6 Update uninstallation 2017-07-11 00:55:53 +08:00
topjohnwu
493642eb38 Minor translation update 2017-07-11 00:55:44 +08:00
gh2923
28d42b9164 fix some expressions 2017-07-08 11:17:41 -05:00
Jens Lody
42f29062ca Fix timeout of temporary granted su-rights. 2017-07-08 11:17:07 -05:00
topjohnwu
c4377ed6c2 Bump version 2017-07-03 01:08:54 +08:00
topjohnwu
7d283ed65f Optimize imports 2017-07-01 18:09:34 +08:00
topjohnwu
bf1f941e50 Adapt to Android O new broadcast limitations 2017-07-01 18:09:34 +08:00
topjohnwu
789fef34ba Fix crash on Android O 2017-07-01 18:09:34 +08:00
topjohnwu
1daf5a611c MagiskHide now defaults to enabled 2017-07-01 17:38:33 +08:00
topjohnwu
6aed1db67e Update Android Studio 2017-07-01 15:57:49 +08:00
gh2923
cf68854770 Update Simplified Chinese Translation 2017-06-20 21:46:36 +08:00
linar10
711392c73b Update Strings PL 2017-06-20 21:45:46 +08:00
c727
9573c32481 update strings.de 2017-06-20 21:45:38 +08:00
RoySchutte
a15f80f79d Create strings.xml 2017-06-20 21:45:28 +08:00
Igor Sorocean
23e7475f06 update romanian translation 2017-06-20 21:45:11 +08:00
topjohnwu
1eb571b787 Proper handle policy changes 2017-06-20 18:33:50 +08:00
topjohnwu
dd3b716d85 Extract expandable viewholder 2017-06-20 17:57:17 +08:00
topjohnwu
28649c07e3 SU policy DB bug fix 2017-06-20 17:57:17 +08:00
topjohnwu
961e02be0d Update Android Studio 2017-06-20 17:54:40 +08:00
topjohnwu
a161491bfd Disable shrinkResources due to buildtool bug 2017-06-16 15:25:22 +08:00
topjohnwu
e0b4d1c1e4 Bump version 2017-06-16 04:07:10 +08:00
topjohnwu
fd4aaab137 Rewrite zip signing 2017-06-16 03:12:57 +08:00
topjohnwu
42d14d5ca2 Update to new build tools, target API 26 2017-06-16 03:06:22 +08:00
topjohnwu
d3ff482c9b Bump version 2017-06-08 22:55:48 +08:00
topjohnwu
f682368eeb Update strings 2017-06-08 22:49:26 +08:00
topjohnwu
4a5d033efb Store data in intent for OTA 2017-06-08 22:35:30 +08:00
topjohnwu
343161b195 Add mount namespace options 2017-06-08 22:27:24 +08:00
topjohnwu
bc576a9659 Update uninstall script 2017-06-08 04:28:55 +08:00
topjohnwu
19e407fcc4 Update translations 2017-06-08 04:23:17 +08:00
RoySchutte
bc7327d004 Update strings.xml 2017-06-08 04:14:12 +08:00
ROBERTO
666fa1c797 Update Italian translation 2017-06-08 04:14:01 +08:00
Igor Sorocean
0eda4a7821 Update romanian translation 2017-06-08 04:13:44 +08:00
topjohnwu
862058fd2b Bump version 2017-06-08 03:20:04 +08:00
topjohnwu
69e5bcd57d Simple OTA implementation 2017-06-07 02:21:58 +08:00
topjohnwu
efeddda328 Use Java synchronize instead serial tasks 2017-06-06 03:21:52 +08:00
topjohnwu
ff6938280e Switch to DB based su configs 2017-06-01 03:18:41 +08:00
RoySchutte
1e4425b30f Update strings-nl.xml 2017-05-31 11:45:02 -05:00
Igor Sorocean
b5d1d8cdad Update romanian translation 2017-05-31 11:44:37 -05:00
gh2923
029be5ccca Update Simplified Chinese Translation 2017-05-31 11:44:17 -05:00
gh2923
29c2d785b5 Update Simplified Chinese Translation 2017-05-31 11:44:04 -05:00
Exalm
abda8cfa32 Updated russian translation 2017-05-31 11:43:48 -05:00
topjohnwu
44e7d79d4c Add Arabic translation
Credits to @xx6600xx
2017-06-01 00:41:36 +08:00
topjohnwu
9a1dc8ee0e Refactor su database 2017-06-01 00:26:36 +08:00
topjohnwu
27879c3f01 Improve Logger 2017-05-31 17:43:55 +08:00
topjohnwu
29096eb5d7 Monitor package (un)install events 2017-05-31 16:31:33 +08:00
topjohnwu
a573baea03 Simplify SU requests, binary should be much superior now 2017-05-30 01:27:10 +08:00
topjohnwu
5af07c4531 Update Traditional Chinese translate 2017-05-28 01:44:29 +08:00
topjohnwu
44e36feb09 Improve multiuser settings and notification 2017-05-28 01:31:19 +08:00
topjohnwu
2a7d996881 Add multiuser support 2017-05-27 02:41:24 +08:00
topjohnwu
738f943a68 Several UI tweaks 2017-05-26 18:20:53 +08:00
dvdandroid
47e62a5681 Small code cleanup 2017-05-24 21:21:15 +02:00
dvdandroid
1ecbfd7590 Adjust theme in about and settings activities 2017-05-24 20:55:47 +02:00
topjohnwu
67c139a04b Fix theme changing glitch 2017-05-24 00:37:15 +08:00
RoySchutte
31cc008249 Update strings.xml
2 small changes to make strings more similar.
2017-05-23 19:47:38 +08:00
topjohnwu
9cb026439d Update translations 2017-05-23 17:02:05 +08:00
topjohnwu
e6f10176c6 Network check 2017-05-23 17:01:38 +08:00
RoySchutte
0917c79470 Update strings.xml
Added and translated new strings.
2017-05-22 23:53:59 +08:00
ROBERTO
597baa986d Updated Italian language 2017-05-22 23:53:43 +08:00
topjohnwu
75cc4b4843 Merge install and status 2017-05-21 12:16:38 +08:00
topjohnwu
aac088d496 Update strings.xml 2017-05-20 03:17:37 +08:00
RoySchutte
a822e5bbc5 Update strings.xml
Fixed many Dutch translations which were gramatically incorrect. Added translations (up-to-date).
Hopefully these translations will make it to the next release, because the current translations aren't pretty *_*.
2017-05-20 03:08:22 +08:00
Igor Sorocean
c527249c21 Add romanian translation 2017-05-20 03:08:14 +08:00
topjohnwu
9ef798f534 Update SafetyNet check UI 2017-05-20 03:04:14 +08:00
topjohnwu
e69b99f089 Update status UI 2017-05-19 08:37:57 -07:00
topjohnwu
55b8079e86 Update MagiskHide method 2017-05-12 23:11:28 +08:00
topjohnwu
e272dbe9af Include busybox binary and remove busybox toggle 2017-05-12 04:05:21 +08:00
topjohnwu
962f8354ac Use new version detection method 2017-05-12 02:25:07 +08:00
topjohnwu
20e4a960f7 Fix strings 2017-05-10 22:54:17 +08:00
ROBERTO
82249cb50a Italian language update 2017-04-28 23:45:41 +08:00
gh2923
fad417e553 Update Simplified Chinese Translation 2017-04-28 15:41:59 +08:00
lindwurm
5ba692f50c l10n: Update Japanese Translations
* Fixed more strings!

Signed-off-by: lindwurm <lindwurm.q@gmail.com>
2017-04-28 15:41:50 +08:00
topjohnwu
907e01e524 Use stable build tools + retrolambda 2017-04-26 19:04:06 +08:00
lindwurm
b8ed23efa7 l10n: Update Japanese Translations
Signed-off-by: lindwurm <lindwurm.q@gmail.com>
2017-04-26 19:03:14 +08:00
topjohnwu
2b3bbf7e67 Bump version 2017-04-26 00:59:56 +08:00
topjohnwu
464fe627a3 Swap tabs 2017-04-26 00:27:55 +08:00
topjohnwu
6a9e39c470 Support unlimited amount of repos 2017-04-26 00:15:53 +08:00
topjohnwu
7fec9a3cc6 Fix string.xml errors 2017-04-24 22:26:40 +08:00
Primokorn
008f6ef462 Update french strings.xml
Better translation.
2017-04-24 21:54:34 +08:00
lilymaniac
2440c108ca Update values-ko/strings.xml 2017-04-24 21:54:22 +08:00
linar10
430baad8a4 Update strings pl 2017-04-24 21:54:05 +08:00
Nosi
51132e74b4 Changes Spanish 2017-04-24 21:53:48 +08:00
killer7Mod
a4f33e106a Update Portuguese translation 2017-04-24 21:53:21 +08:00
SakuraSa233
baba3190e0 Add Japanese Translation 2017-04-24 21:53:05 +08:00
topjohnwu
47b13aa5ea Use stock FAB; Log monospace; Fixes 2017-04-24 21:52:23 +08:00
topjohnwu
ae88d3054d Finally, official Java 8 support 2017-04-05 17:02:18 +08:00
topjohnwu
411b600e14 Screw that Jack compiler, use retrolambda 2017-03-31 16:04:12 +08:00
topjohnwu
0a0ad9a184 Bump to 4.3.1 2017-03-31 13:17:58 +08:00
topjohnwu
234bead59e Bump version 2017-03-31 06:58:47 +08:00
Primokorn
76de310986 Create french strings.xml
Hope it's not too late for the update :)
2017-03-31 03:23:23 +08:00
topjohnwu
817f050bcd Say goodbye to old modules 2017-03-30 06:52:18 +08:00
topjohnwu
60ae685d1e Change disable to Core Only Mode 2017-03-30 05:16:50 +08:00
Wang Han
4c7bdbb284 Fix crashing when selecting release notes on some devices 2017-03-26 23:55:11 +08:00
topjohnwu
435251ca41 Bump version 2017-03-20 06:24:59 +08:00
topjohnwu
324a0dd38f Update uninstall script 2017-03-20 04:17:04 +08:00
topjohnwu
cc77d93918 Fix string.xml(vi) 2017-03-20 03:38:24 +08:00
Nguyễn Thanh Tài
0ea7d8bd8c Added Vietnamese translation 2017-03-20 03:12:03 +08:00
topjohnwu
849b217143 Fix build issues 2017-03-16 14:08:40 +08:00
Fabio
9af6efba59 Update Italian Translation [2/2] 2017-03-16 13:40:52 +08:00
Fatih Fırıncı
079d6f06ef Added turkish language
Please merge it
2017-03-16 13:40:43 +08:00
gargamelek
9cf0757689 Added czech translation 2017-03-16 13:40:30 +08:00
c727
b54c438948 update strings-de 2017-03-16 13:40:10 +08:00
linar10
c3ff4bfdad Update strings pl 2017-03-16 13:39:49 +08:00
topjohnwu
5d62e066e2 Bump version 2017-02-22 05:06:19 +08:00
topjohnwu
e94219c5a3 Add notification settings 2017-02-22 04:58:03 +08:00
topjohnwu
8ed9634adf Fix Samsung crash 2017-02-22 04:13:21 +08:00
topjohnwu
0aefa9599f Version bump 2017-02-21 03:52:35 +08:00
c727
e279cf0575 update strings-de 2017-02-20 13:40:01 -06:00
topjohnwu
a3f0ef8e77 Many improvements and bug fixes
Close #114
2017-02-21 03:38:37 +08:00
topjohnwu
8eba05ed4a Potentially fix Samsung crash and change colors 2017-02-20 20:11:07 +08:00
topjohnwu
2f78155723 Bump version 2017-02-19 10:49:47 +08:00
topjohnwu
6785221479 Small refinements and bugfixes
Close #109
2017-02-19 10:14:29 +08:00
topjohnwu
9bc410dd3d Add MarkDown styles 2017-02-18 04:35:51 +08:00
gh2923
2491ab6bf9 Update Simplified Chinese Translation 2017-02-17 10:56:44 -06:00
topjohnwu
f615ed40cd Several refinements 2017-02-17 14:07:15 +08:00
linar10
430f2cafc1 Update strings.xml 2017-02-16 23:27:51 -06:00
Deiki-kun
0ad049da88 Updated and corrected Spanish strings.xml 2017-02-16 23:27:39 -06:00
c727
2c7691567b Update strings-de 2017-02-16 23:27:22 -06:00
topjohnwu
1d70d0fe94 Don't show notification again if coming from notification 2017-02-17 09:26:27 +08:00
topjohnwu
ac44f05811 Resource cleanup 2017-02-17 09:03:40 +08:00
topjohnwu
d99252f394 Add update notification 2017-02-17 08:51:51 +08:00
topjohnwu
b58c7ba7c5 Add download button to repo, close #99 2017-02-16 17:50:36 +08:00
topjohnwu
8c5acd1a0a Add traditional Chinese 2017-02-16 17:09:11 +08:00
linar10
b9b1ebf18c Update strings.xml 2017-02-16 01:44:37 -06:00
lilymaniac
8ca132cef0 Add Korean translation
Change-Id: Ie5b9ee02dc179c99b1ff5c50e5ce046cc2f2522e
Signed-off-by: lilymaniac <lilymaniac@outlook.com>
2017-02-16 01:43:46 -06:00
topjohnwu
a03bb90754 Use README.md in details for repo 2017-02-16 05:48:26 +08:00
topjohnwu
d1c939f48a Use temporary files to process zips
Fix #96
2017-02-15 23:46:50 +08:00
gh2923
21b11f1b48 Update Simplified Chinese Translation 2017-02-15 08:44:45 +08:00
topjohnwu
23c84a7803 Massive Zip flashing refactoring 2017-02-15 05:25:24 +08:00
topjohnwu
f9ab060403 Fix su request crashing 2017-02-15 05:07:14 +08:00
topjohnwu
df7a5bf149 Redo styling 2017-02-14 16:35:03 +08:00
topjohnwu
c4afa069df Add custom AlertDialog 2017-02-13 23:11:50 +08:00
topjohnwu
1bfafdb44f Don't reload ApplicationInfo list
Fix #94
2017-02-13 04:00:45 +08:00
topjohnwu
1ef5bd7076 Remove URL in resources 2017-02-13 03:16:39 +08:00
linar10
29176fa4f4 Update strings-pl 2017-02-13 03:14:24 +08:00
topjohnwu
958c95732b Move AboutCardRow to components package 2017-02-13 03:13:24 +08:00
topjohnwu
44b0d4127c Remove GSON and switch to database 2017-02-12 23:27:20 +08:00
topjohnwu
1418ec2416 Remove module helper 2017-02-12 20:53:41 +08:00
topjohnwu
b51978f51c Move asynctasks to seperate package 2017-02-12 19:49:46 +08:00
topjohnwu
b07361580a Contexts are different: Make context clearer 2017-02-12 05:02:18 +08:00
topjohnwu
d1b5ebad7d Several fixes 2017-02-07 07:32:40 +08:00
Exalm
f4ce813de9 Better icon 2017-02-07 06:17:54 +08:00
drbeat
b44ac994d8 fix typos and translate new strings 2017-02-07 06:16:53 +08:00
Exalm
333948814c Russian translation 2017-02-07 06:16:14 +08:00
linar10
1a51ad6e01 Update strings - pl 2017-02-07 06:15:52 +08:00
topjohnwu
22a5c11f0d Fix MagiskHide startup issue 2017-02-07 06:02:06 +08:00
topjohnwu
51b22d1ad4 Make callback events non-static 2017-02-07 04:09:49 +08:00
topjohnwu
bef5969580 No more static crap :) 2017-02-07 02:01:32 +08:00
topjohnwu
c6bf7bb9cd Bump version 2017-02-06 08:34:55 +08:00
linar10
2a84d92cbf Update strings.xml 2017-02-06 08:32:20 +08:00
linar10
62de36b0da Update strings - pl last changes 2017-02-06 08:32:20 +08:00
c727
03a9aaeff7 strings-de latest changes for release 2017-02-06 08:32:07 +08:00
topjohnwu
45765e292d Final fixes 2017-02-06 08:16:48 +08:00
topjohnwu
6e28a26015 Add uninstall button 2017-02-06 03:20:17 +08:00
topjohnwu
9150bf720d Add info for MagiskHide when not using MagiskSU
Close #63
2017-02-06 03:20:16 +08:00
topjohnwu
845864679c Allow multi lines
Fix #53
2017-02-05 22:05:44 +08:00
topjohnwu
b3b2149ebb Optimize root shell and startups 2017-02-05 22:02:14 +08:00
topjohnwu
0886dca385 string.xml update 2017-02-05 04:46:59 +08:00
c727
53198ba4a7 update for strings-de 2017-02-05 04:42:31 +08:00
killer7mod
a9652ee1fd update strings.xml PT 2017-02-05 04:42:22 +08:00
gh2923
75caf2f01c Update Simplified Chinese Translation 2017-02-05 04:42:04 +08:00
linar10
65bab2666e Update strings.xml -PL 2017-02-05 04:41:54 +08:00
Fabio
6d93ae399a Update italian Translation [1/2] 2017-02-05 04:41:41 +08:00
topjohnwu
7239c2e31a Update to the latest settings 2017-02-05 04:40:52 +08:00
topjohnwu
43b7ef8110 Add disable, change busybox 2017-02-02 19:19:22 +08:00
topjohnwu
99ef0b8cb4 Handle MagiskHide at boot 2017-02-01 23:54:32 +08:00
topjohnwu
0efb4da0ee Several bug fixes
Fix #57
2017-01-31 03:39:24 +08:00
linar10
ed7920d61e Added missing entries for strings-pl 2017-01-30 20:12:53 +08:00
c727
c0379c8e25 update strings-de to "Add Superuser settings" 2017-01-30 20:11:56 +08:00
tonymanou
00a0e64fdd Prefer List/Map/Set as declaring type over their implementations
Unless your are using a method declared in subclasses of an
interface, it is better to use the interface as declaring type.
One advantage of this is that changing used implementation will
be much simpler (you will have less declarations to edit).
2017-01-30 20:11:17 +08:00
tonymanou
0dc60debea Fix warning about use of API limited to support package 2017-01-30 20:11:17 +08:00
tonymanou
c44ae5888c Optimize map operations 2017-01-30 20:11:17 +08:00
topjohnwu
b9495cd1bb Improve static data management 2017-01-30 20:04:49 +08:00
topjohnwu
bfec381933 Improve su requests 2017-01-30 19:27:00 +08:00
topjohnwu
2dddb8df69 Reset menu every transaction 2017-01-30 01:51:55 +08:00
topjohnwu
d30397e9c0 Let users know why blacklist PoGO and AP... 2017-01-30 01:40:51 +08:00
topjohnwu
d9597549fd Prevent excessive su requests 2017-01-30 00:44:33 +08:00
topjohnwu
13512b4146 Add BootReceiver 2017-01-29 16:52:43 +08:00
topjohnwu
49e546919a Update logs 2017-01-29 16:20:41 +08:00
topjohnwu
586015c2ed Fix ButterKnife issue
https://code.google.com/p/android/issues/detail?id=231597
2017-01-29 10:27:06 +08:00
topjohnwu
4a7e067d1a Use support library 2017-01-29 00:20:43 +08:00
topjohnwu
9bc0b7f183 Update settings 2017-01-28 22:02:33 +08:00
topjohnwu
cd4dfc9861 Add Superuser settings 2017-01-28 06:13:07 +08:00
topjohnwu
09bdbc1224 Revert "Read only the first line instead of loading the whole file"
This reverts commit a5b573eaaa.

The file shall always have one single line, no need to create a new method
2017-01-28 01:25:51 +08:00
tonymanou
978b3a64c5 Remove context reference from recyclerview adapter 2017-01-28 01:25:15 +08:00
tonymanou
651547ef20 Fix raw use of generics warnings 2017-01-28 01:25:15 +08:00
tonymanou
b4d95977d0 Remove redundant XML namespaces 2017-01-28 01:25:15 +08:00
tonymanou
5d8bb897db Separate JNI glue from actual C code, move CMakeLists file 2017-01-28 01:25:15 +08:00
tonymanou
84c8ecb372 Slight improvement for the navigation drawer 2017-01-28 01:25:15 +08:00
tonymanou
61abe5b948 Do not close the whole application in case of error 2017-01-28 01:25:15 +08:00
tonymanou
a5b573eaaa Read only the first line instead of loading the whole file 2017-01-28 01:25:15 +08:00
topjohnwu
cbb32f82eb Add Superuser logging UI 2017-01-28 01:13:28 +08:00
topjohnwu
ca9334b2df Add tabs to log fragment 2017-01-27 03:43:37 +08:00
topjohnwu
959ed7f866 Implement logging and bug fixes 2017-01-27 01:02:40 +08:00
c727
a5c0411be0 update strings-de 2017-01-26 14:28:27 +08:00
linar10
32e1303742 Add Polish translate 2017-01-26 14:28:04 +08:00
topjohnwu
7263b6fe89 Handle bootblock detect failure cases 2017-01-26 14:25:12 +08:00
topjohnwu
46a4070f84 Prevent shell response crashes 2017-01-26 13:46:54 +08:00
topjohnwu
c3c155a1ed Improved settings 2017-01-26 04:17:51 +08:00
topjohnwu
b067105660 Fix bug where no info is available 2017-01-26 03:45:05 +08:00
topjohnwu
15ca18848e Add su revoke 2017-01-26 03:30:12 +08:00
topjohnwu
67c9e2ead6 Add Superuser management UI 2017-01-26 01:13:23 +08:00
topjohnwu
3681177be4 Rename fragment layouts 2017-01-25 17:07:23 +08:00
topjohnwu
6eb814ef0b Fix some small issues 2017-01-25 16:45:55 +08:00
topjohnwu
bcc695234c Seperate Configs 2017-01-25 13:17:33 +08:00
topjohnwu
ad16a6fc1b Project restructure 2017-01-25 04:33:22 +08:00
topjohnwu
478b7eeb65 Stop countdown when user reacts 2017-01-25 02:16:36 +08:00
topjohnwu
151a153dc9 Fix toasts and timeouts 2017-01-25 01:23:41 +08:00
topjohnwu
ad131854ca Update request popup UI 2017-01-25 01:01:12 +08:00
topjohnwu
0bd0eb9e59 Magisk Manager is now a SU client
1. Add request popup
2. Add su request notifications
3. Add su database helpers
2017-01-24 14:19:28 +08:00
c727
cf16fd0104 update strings-de for Magisk Manager 3.1 2017-01-15 02:37:58 +08:00
tonymanou
21b00ac6ca Use try-with-resources in some places 2017-01-15 02:37:40 +08:00
tonymanou
57e6f3080c Fix generic type 2017-01-15 02:37:40 +08:00
tonymanou
89744100ce Remove unnecessary Butterknife binding in adapters 2017-01-15 02:37:40 +08:00
tonymanou
a718f9bbfd Unbind Butterknife-injected views in fragment's onDestroyView() 2017-01-15 02:37:40 +08:00
tonymanou
e81bc4f044 Clean up main activity code
No need to catch IllegalStateException as we display the fragment from
onCreate() without delay.
2017-01-15 02:37:40 +08:00
tonymanou
4dbacd79ae Matching event [un]registering, call super at the end of onPause/onDestroy
Event unregistered in onDestroy() should be registered in onCreate() to
avoid being registered multiple times.
2017-01-15 02:37:40 +08:00
tonymanou
ae74d54451 Events should be final in order to work 2017-01-15 02:37:40 +08:00
tonymanou
dc316c5669 Set fragment title and [un]register callbacks in onStart/onStop
onStart() is called when the fragment is made visible, whereas onPause()
is called when the fragment looses focus e.g. if a dialog is shown.
Thus:
- there is no need to set the activity's title everytime the fragment
regains focus,
- it is better to listen to event tasks and refresh the state of the UI
while the fragment is actually visible, listening to events until the
fragment is destroyed is useless: if an event is received between
onStop() and onDestroy(), there will be some processing but nothing will
be shown because the fragment is no longer visible.
2017-01-15 02:37:40 +08:00
tonymanou
e9f04256c9 setHasOptionsMenu() should be called from fragment's onCreate() 2017-01-15 02:37:40 +08:00
321 changed files with 16598 additions and 7589 deletions

8
.gitignore vendored
View File

@@ -3,6 +3,10 @@
/local.properties
.idea/
/build
app/app-release.apk
app/release
*.hprof
app/.externalNativeBuild/
.externalNativeBuild/
src/full/res/raw/util_functions.sh
public.certificate.x509.pem
private.key.pk8
*.apk

View File

@@ -1,4 +1,7 @@
# Magisk Manager
The project should be built with Android Studio version 2.2.0+
I use Java 8 features, which requires Jack compiler and it's only available in 2.2.0+
Also, you need to install CMake and NDK to build the zipadjust library for zip preprocessing
This repo is no longer an independent component. It is a submodule of the [Magisk Project](https://github.com/topjohnwu/Magisk).
# Translations
The default (English) string resources are scattered in these files: `src/full/res/values/strings.xml`, `src/main/res/values/strings.xml`, `src/stub/res/values/strings.xml`.
Place the translated XMLs in the corresponding folder to the locale.
Translations are highly appreciated via pull requests here on Github.

1
app/.gitignore vendored
View File

@@ -1 +0,0 @@
/build

View File

@@ -1,5 +0,0 @@
cmake_minimum_required(VERSION 3.6)
add_library(zipadjust SHARED src/main/jni/zipadjust.c)
find_library(libz z)
find_library(liblog log)
target_link_libraries(zipadjust ${libz} ${liblog})

View File

@@ -1,64 +0,0 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.topjohnwu.magisk"
minSdkVersion 21
targetSdkVersion 25
versionCode 12
versionName "3.1"
jackOptions {
enabled true
}
ndk {
moduleName 'zipadjust'
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
}
}
compileOptions {
incremental false
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
dexOptions {
preDexLibraries = false
}
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
}
}
}
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:recyclerview-v7:25.1.0'
compile 'com.android.support:cardview-v7:25.1.0'
compile 'com.android.support:design:25.1.0'
compile 'com.jakewharton:butterknife:8.4.0'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.github.clans:fab:1.6.4'
compile 'com.madgag.spongycastle:core:1.54.0.0'
compile 'com.madgag.spongycastle:prov:1.54.0.0'
compile 'com.madgag.spongycastle:pkix:1.54.0.0'
compile 'com.madgag.spongycastle:pg:1.54.0.0'
compile 'com.google.android.gms:play-services-safetynet:9.0.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
}

View File

@@ -1,46 +0,0 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/topjohnwu/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.topjohnwu.magisk.module.** { *; }
-keep class com.topjohnwu.magisk.utils.ModuleHelper$ValueSortedMap { *; }
# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
-keep class android.support.v7.internal.** { *; }
-keep interface android.support.v7.internal.** { *; }
-keep class android.support.v7.** { *; }
-keep interface android.support.v7.** { *; }
# SpongyCastle
-keep class org.spongycastle.** {*;}

View File

@@ -1,61 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.topjohnwu.magisk"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:exported="true"/>
<activity
android:name=".SplashActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".AboutActivity"
android:theme="@style/AppTheme.Transparent"/>
<activity
android:name=".SettingsActivity"
android:theme="@style/AppTheme.Transparent" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.topjohnwu.magisk.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
</application>
</manifest>

Binary file not shown.

View File

@@ -1,27 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD
VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
Fw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM
qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4
wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy
4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU
RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s
zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw
HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ
AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud
EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa
J6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y
LCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe
+ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX
31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr
sBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0=
-----END CERTIFICATE-----

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -1,152 +0,0 @@
package com.topjohnwu.magisk;
import android.app.AlertDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
import java.io.IOException;
import java.io.InputStream;
import butterknife.BindView;
import butterknife.ButterKnife;
public class AboutActivity extends AppCompatActivity {
private static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/MagiskManager";
private static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
private static final String DONATION_URL = "http://topjohnwu.github.io/donate";
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.app_version_info) AboutCardRow appVersionInfo;
@BindView(R.id.app_changelog) AboutCardRow appChangelog;
@BindView(R.id.app_developers) AboutCardRow appDevelopers;
@BindView(R.id.app_translators) AboutCardRow appTranslators;
@BindView(R.id.app_source_code) AboutCardRow appSourceCode;
@BindView(R.id.support_thread) AboutCardRow supportThread;
@BindView(R.id.donation) AboutCardRow donation;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
Logger.dev("AboutActivity: Theme is " + theme);
if (Utils.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
setContentView(R.layout.activity_about);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(R.string.about);
ab.setDisplayHomeAsUpEnabled(true);
}
appVersionInfo.setSummary(BuildConfig.VERSION_NAME);
String changes = null;
try {
InputStream is = getAssets().open("changelog.html");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
changes = new String(buffer);
} catch (IOException ignored) {
}
appChangelog.removeSummary();
if (changes == null) {
appChangelog.setVisibility(View.GONE);
} else {
Spanned result;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
result = Html.fromHtml(changes, Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
} else {
result = Html.fromHtml(changes);
}
appChangelog.setOnClickListener(v -> {
AlertDialog d = Utils.getAlertDialogBuilder(this)
.setTitle(R.string.app_changelog)
.setMessage(result)
.setPositiveButton(android.R.string.ok, null)
.show();
//noinspection ConstantConditions
((TextView) d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
});
}
appDevelopers.removeSummary();
appDevelopers.setOnClickListener(view -> {
Spanned result;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
result = Html.fromHtml(getString(R.string.app_developers_), Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
} else {
result = Html.fromHtml(getString(R.string.app_developers_));
}
AlertDialog d = Utils.getAlertDialogBuilder(this)
.setTitle(R.string.app_developers)
.setMessage(result)
.setPositiveButton(android.R.string.ok, null)
.create();
d.show();
//noinspection ConstantConditions
((TextView) d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
});
String translators = getString(R.string.translators);
if (TextUtils.isEmpty(translators)) {
appTranslators.setVisibility(View.GONE);
} else {
appTranslators.setSummary(translators);
}
appSourceCode.removeSummary();
appSourceCode.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(SOURCE_CODE_URL))));
supportThread.removeSummary();
supportThread.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(XDA_THREAD))));
donation.removeSummary();
donation.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(DONATION_URL))));
setFloating();
}
public void setFloating() {
boolean isTablet = getResources().getBoolean(R.bool.isTablet);
if (isTablet) {
WindowManager.LayoutParams params = getWindow().getAttributes();
params.height = getResources().getDimensionPixelSize(R.dimen.floating_height);
params.width = getResources().getDimensionPixelSize(R.dimen.floating_width);
params.alpha = 1.0f;
params.dimAmount = 0.6f;
params.flags |= 2;
getWindow().setAttributes(params);
setFinishOnTouchOutside(true);
}
}
}

View File

@@ -1,113 +0,0 @@
package com.topjohnwu.magisk;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.util.AttributeSet;
import android.view.View;
import com.github.clans.fab.FloatingActionMenu;
import java.util.List;
/**
* Created by Matteo on 08/08/2015.
*
* Floating Action Menu Behavior for Clans.FloatingActionButton
* https://github.com/Clans/FloatingActionButton/
*
* Use this behavior as your app:layout_behavior attribute in your Floating Action Menu to use the
* FabMenu in a Coordinator Layout.
*
* Remember to use the correct namespace for the fab:
* xmlns:fab="http://schemas.android.com/apk/res-auto"
*/
public class FABBehavior extends CoordinatorLayout.Behavior {
private float mTranslationY;
public FABBehavior(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
if (dependency instanceof Snackbar.SnackbarLayout) {
updateTranslation(parent, child);
}
return false;
}
@Override
public void onDependentViewRemoved(CoordinatorLayout parent, View child, View dependency) {
if (dependency instanceof Snackbar.SnackbarLayout) {
revertTranslation(child);
}
}
private void updateTranslation(CoordinatorLayout parent, View child) {
float translationY = getTranslationY(parent, child);
if (translationY != mTranslationY) {
ViewPropertyAnimatorCompat anim = ViewCompat.animate(child);
anim.cancel();
anim.translationY(translationY).setDuration(100);
mTranslationY = translationY;
}
}
private void revertTranslation(View child) {
if (mTranslationY != 0) {
ViewPropertyAnimatorCompat anim = ViewCompat.animate(child);
anim.cancel();
anim.translationY(0).setDuration(100);
mTranslationY = 0;
}
}
private float getTranslationY(CoordinatorLayout parent, View child) {
float minOffset = 0.0F;
List dependencies = parent.getDependencies(child);
int i = 0;
for (int z = dependencies.size(); i < z; ++i) {
View view = (View) dependencies.get(i);
if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(child, view)) {
minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - (float) view.getHeight());
}
}
return minOffset;
}
/**
* onStartNestedScroll and onNestedScroll will hide/show the FabMenu when a scroll is detected.
*/
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child,
View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL ||
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target,
nestedScrollAxes);
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed,
dyUnconsumed);
FloatingActionMenu fabMenu = (FloatingActionMenu) child;
if (dyConsumed > 0 && !fabMenu.isMenuButtonHidden()) {
fabMenu.hideMenuButton(true);
} else if (dyConsumed < 0 && fabMenu.isMenuButtonHidden()) {
fabMenu.showMenuButton(true);
}
}
}

View File

@@ -1,112 +0,0 @@
package com.topjohnwu.magisk;
import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.CardView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.TextView;
import com.topjohnwu.magisk.receivers.MagiskDlReceiver;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class InstallFragment extends Fragment implements CallbackHandler.EventListener {
public static final CallbackHandler.Event blockDetectionDone = new CallbackHandler.Event();
public static List<String> blockList;
public static String bootBlock = null;
@BindView(R.id.current_version_title) TextView currentVersionTitle;
@BindView(R.id.install_title) TextView installTitle;
@BindView(R.id.block_spinner) Spinner spinner;
@BindView(R.id.detect_bootimage) Button detectButton;
@BindView(R.id.flash_button) CardView flashButton;
@BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
@BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.install_fragment, container, false);
ButterKnife.bind(this, v);
detectButton.setOnClickListener(v1 -> toAutoDetect());
currentVersionTitle.setText(getString(R.string.current_magisk_title, StatusFragment.magiskVersionString));
installTitle.setText(getString(R.string.install_magisk_title, StatusFragment.remoteMagiskVersion));
flashButton.setOnClickListener(v1 -> {
String bootImage = bootBlock;
if (bootImage == null) {
bootImage = blockList.get(spinner.getSelectedItemPosition() - 1);
}
String filename = "Magisk-v" + StatusFragment.remoteMagiskVersion + ".zip";
String finalBootImage = bootImage;
Utils.getAlertDialogBuilder(getActivity())
.setTitle(getString(R.string.repo_install_title, getString(R.string.magisk)))
.setMessage(getString(R.string.repo_install_msg, filename))
.setCancelable(true)
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
getActivity(),
new MagiskDlReceiver(finalBootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()),
StatusFragment.magiskLink,
Utils.getLegalFilename(filename)))
.setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(StatusFragment.releaseNoteLink)));
})
.setNegativeButton(R.string.no_thanks, null)
.show();
});
if (blockDetectionDone.isTriggered) {
updateUI();
}
return v;
}
@Override
public void onTrigger(CallbackHandler.Event event) {
updateUI();
}
private void updateUI() {
List<String> items = new ArrayList<>(blockList);
items.add(0, getString(R.string.auto_detect, bootBlock));
ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
toAutoDetect();
}
private void toAutoDetect() {
if (bootBlock != null) {
spinner.setSelection(0);
}
}
@Override
public void onResume() {
super.onResume();
getActivity().setTitle(R.string.install);
CallbackHandler.register(blockDetectionDone, this);
}
@Override
public void onDestroy() {
super.onDestroy();
CallbackHandler.unRegister(blockDetectionDone, this);
}
}

View File

@@ -1,236 +0,0 @@
package com.topjohnwu.magisk;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Fragment;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class LogFragment extends Fragment {
private static final String MAGISK_LOG = "/cache/magisk.log";
@BindView(R.id.txtLog) TextView txtLog;
@BindView(R.id.svLog) ScrollView svLog;
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
@BindView(R.id.progressBar) ProgressBar progressBar;
private MenuItem mClickedMenuItem;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.log_fragment, container, false);
ButterKnife.bind(this, view);
txtLog.setTextIsSelectable(true);
new LogManager().read();
return view;
}
@Override
public void onResume() {
super.onResume();
setHasOptionsMenu(true);
new LogManager().read();
getActivity().setTitle(R.string.log);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
mClickedMenuItem = item;
switch (item.getItemId()) {
case R.id.menu_refresh:
new LogManager().read();
return true;
case R.id.menu_send:
new LogManager().send();
return true;
case R.id.menu_save:
new LogManager().save();
return true;
case R.id.menu_clear:
new LogManager().clear();
return true;
default:
return true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (mClickedMenuItem != null) {
new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
}
} else {
Snackbar.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
}
}
}
public class LogManager extends Async.RootTask<Object, Void, Object> {
int mode;
File targetFile;
@SuppressLint("DefaultLocale")
@Override
protected Object doInBackground(Object... params) {
mode = (int) params[0];
switch (mode) {
case 0:
List<String> logList = Utils.readFile(MAGISK_LOG);
StringBuilder llog = new StringBuilder(15 * 10 * 1024);
for (String s : logList) {
llog.append(s).append("\n");
}
return llog.toString();
case 1:
Shell.su("echo > " + MAGISK_LOG);
Snackbar.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
return "";
case 2:
case 3:
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
return false;
}
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
return false;
Calendar now = Calendar.getInstance();
String filename = String.format(
"magisk_%s_%04d%02d%02d_%02d%02d%02d.log", "error",
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
targetFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/MagiskManager/" + filename);
if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs())
|| (targetFile.exists() && !targetFile.delete()))
return false;
List<String> in = Utils.readFile(MAGISK_LOG);
try {
FileWriter out = new FileWriter(targetFile);
for (String line : in) {
out.write(line + "\n");
}
out.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return null;
}
@Override
protected void onPostExecute(Object o) {
boolean bool;
String llog;
switch (mode) {
case 0:
case 1:
llog = (String) o;
progressBar.setVisibility(View.GONE);
if (llog.length() == 0)
txtLog.setText(R.string.log_is_empty);
else
txtLog.setText(llog);
svLog.post(() -> svLog.scrollTo(0, txtLog.getHeight()));
hsvLog.post(() -> hsvLog.scrollTo(0, 0));
break;
case 2:
bool = (boolean) o;
if (bool)
Toast.makeText(getActivity(), targetFile.toString(), Toast.LENGTH_LONG).show();
else
Toast.makeText(getActivity(), getString(R.string.logs_save_failed), Toast.LENGTH_LONG).show();
break;
case 3:
bool = (boolean) o;
if (bool) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(targetFile));
sendIntent.setType("application/html");
startActivity(Intent.createChooser(sendIntent, getResources().getString(R.string.menuSend)));
} else {
Toast.makeText(getActivity(), getString(R.string.logs_save_failed), Toast.LENGTH_LONG).show();
}
}
}
public void read() {
exec(0);
}
public void clear() {
exec(1);
}
public void save() {
exec(2);
}
public void send() {
exec(3);
}
}
}

View File

@@ -1,116 +0,0 @@
package com.topjohnwu.magisk;
import android.app.Fragment;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SearchView;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import java.util.Arrays;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MagiskHideFragment extends Fragment implements CallbackHandler.EventListener {
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
// Don't show in list...
public static final List<String> BLACKLIST = Arrays.asList(
"android",
"com.topjohnwu.magisk",
"com.google.android.gms",
"com.google.android.apps.walletnfcrel",
"com.nianticlabs.pokemongo"
);
public static CallbackHandler.Event packageLoadDone = new CallbackHandler.Event();
private ApplicationAdapter appAdapter;
private SearchView.OnQueryTextListener searchListener;
private String lastFilter;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.magisk_hide_fragment, container, false);
ButterKnife.bind(this, view);
PackageManager packageManager = getActivity().getPackageManager();
mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> new Async.LoadApps(packageManager).exec());
appAdapter = new ApplicationAdapter(packageManager);
recyclerView.setAdapter(appAdapter);
searchListener = new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
lastFilter = query;
appAdapter.filter(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
lastFilter = newText;
appAdapter.filter(newText);
return false;
}
};
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_magiskhide, menu);
SearchView search = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.app_search));
search.setOnQueryTextListener(searchListener);
}
@Override
public void onResume() {
super.onResume();
setHasOptionsMenu(true);
getActivity().setTitle(R.string.magiskhide);
CallbackHandler.register(packageLoadDone, this);
if (packageLoadDone.isTriggered) {
onTrigger(packageLoadDone);
}
}
@Override
public void onPause() {
super.onPause();
CallbackHandler.unRegister(packageLoadDone, this);
}
@Override
public void onTrigger(CallbackHandler.Event event) {
Logger.dev("MagiskHideFragment: UI refresh");
Async.LoadApps.Result result = (Async.LoadApps.Result) event.getResult();
appAdapter.setLists(result.listApps, result.hideList);
mSwipeRefreshLayout.setRefreshing(false);
if (!TextUtils.isEmpty(lastFilter)) {
appAdapter.filter(lastFilter);
}
}
}

View File

@@ -1,211 +0,0 @@
package com.topjohnwu.magisk;
import android.Manifest;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, CallbackHandler.EventListener {
private static final String SELECTED_ITEM_ID = "SELECTED_ITEM_ID";
public static CallbackHandler.Event recreate = new CallbackHandler.Event();
private final Handler mDrawerHandler = new Handler();
private SharedPreferences prefs;
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.nav_view) public NavigationView navigationView;
@IdRes
private int mSelectedId = R.id.status;
@Override
protected void onCreate(final Bundle savedInstanceState) {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
if (Utils.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
setSupportActionBar(toolbar);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
super.onDrawerSlide(drawerView, 0); // this disables the arrow @ completed tate
}
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, 0); // this disables the animation
}
};
drawer.addDrawerListener(toggle);
toggle.syncState();
//noinspection ResourceType
mSelectedId = savedInstanceState == null ? mSelectedId : savedInstanceState.getInt(SELECTED_ITEM_ID);
navigationView.setCheckedItem(mSelectedId);
if (savedInstanceState == null) {
mDrawerHandler.removeCallbacksAndMessages(null);
mDrawerHandler.postDelayed(() -> navigate(mSelectedId), 250);
}
navigationView.setNavigationItemSelectedListener(this);
}
@Override
protected void onResume() {
super.onResume();
CallbackHandler.register(StatusFragment.updateCheckDone, this);
CallbackHandler.register(recreate, this);
if (StatusFragment.updateCheckDone.isTriggered) {
onTrigger(StatusFragment.updateCheckDone);
}
checkHideSection();
}
@Override
protected void onPause() {
super.onPause();
CallbackHandler.unRegister(StatusFragment.updateCheckDone, this);
}
@Override
protected void onDestroy() {
super.onDestroy();
CallbackHandler.unRegister(recreate, this);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SELECTED_ITEM_ID, mSelectedId);
}
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
finish();
}
}
@Override
public boolean onNavigationItemSelected(@NonNull final MenuItem menuItem) {
mSelectedId = menuItem.getItemId();
mDrawerHandler.removeCallbacksAndMessages(null);
mDrawerHandler.postDelayed(() -> navigate(menuItem.getItemId()), 250);
drawer.closeDrawer(GravityCompat.START);
return true;
}
@Override
public void onTrigger(CallbackHandler.Event event) {
if (event == StatusFragment.updateCheckDone) {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.install).setVisible(StatusFragment.remoteMagiskVersion > 0 &&
Shell.rootAccess());
} else if (event == recreate) {
recreate();
}
}
private void checkHideSection() {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible(StatusFragment.magiskVersion >= 8 &&
prefs.getBoolean("magiskhide", false) && Shell.rootAccess());
menu.findItem(R.id.modules).setVisible(StatusFragment.magiskVersion >= 4 &&
Shell.rootAccess());
menu.findItem(R.id.downloads).setVisible(StatusFragment.magiskVersion >= 4 &&
Shell.rootAccess());
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
menu.findItem(R.id.install).setVisible(Shell.rootAccess());
}
public void navigate(final int itemId) {
Fragment navFragment = null;
String tag = "";
switch (itemId) {
case R.id.status:
tag = "status";
navFragment = new StatusFragment();
break;
case R.id.install:
tag = "install";
navFragment = new InstallFragment();
break;
case R.id.modules:
tag = "modules";
navFragment = new ModulesFragment();
break;
case R.id.downloads:
tag = "downloads";
navFragment = new ReposFragment();
break;
case R.id.magiskhide:
tag = "magiskhide";
navFragment = new MagiskHideFragment();
break;
case R.id.log:
tag = "log";
navFragment = new LogFragment();
break;
case R.id.settings:
startActivity(new Intent(this, SettingsActivity.class));
break;
case R.id.app_about:
startActivity(new Intent(this, AboutActivity.class));
return;
}
if (navFragment != null) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
try {
transaction.replace(R.id.content_frame, navFragment, tag).commit();
} catch (IllegalStateException ignored) {}
}
}
}

View File

@@ -1,120 +0,0 @@
package com.topjohnwu.magisk;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.github.clans.fab.FloatingActionButton;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.ModuleHelper;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ModulesFragment extends Fragment implements CallbackHandler.EventListener {
public static final CallbackHandler.Event moduleLoadDone = new CallbackHandler.Event();
private static final int FETCH_ZIP_CODE = 2;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyTv;
@BindView(R.id.fab) FloatingActionButton fabio;
private List<Module> listModules = new ArrayList<>();
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.modules_fragment, container, false);
ButterKnife.bind(this, view);
fabio.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("application/zip");
startActivityForResult(intent, FETCH_ZIP_CODE);
});
mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.GONE);
new Async.LoadModules().exec();
});
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
mSwipeRefreshLayout.setEnabled(recyclerView.getChildAt(0).getTop() >= 0);
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
if (moduleLoadDone.isTriggered) {
updateUI();
}
return view;
}
@Override
public void onTrigger(CallbackHandler.Event event) {
Logger.dev("ModulesFragment: UI refresh triggered");
updateUI();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FETCH_ZIP_CODE && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file
final Uri uri = data.getData();
new Async.FlashZIP(getActivity(), uri).exec();
}
}
@Override
public void onResume() {
super.onResume();
CallbackHandler.register(moduleLoadDone, this);
getActivity().setTitle(R.string.modules);
}
@Override
public void onDestroy() {
super.onDestroy();
CallbackHandler.unRegister(moduleLoadDone, this);
}
private void updateUI() {
ModuleHelper.getModuleList(listModules);
if (listModules.size() == 0) {
emptyTv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyTv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
recyclerView.setAdapter(new ModulesAdapter(listModules));
}
mSwipeRefreshLayout.setRefreshing(false);
}
}

View File

@@ -1,193 +0,0 @@
package com.topjohnwu.magisk;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SearchView;
import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.adapters.SimpleSectionedRecyclerViewAdapter;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.ModuleHelper;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ReposFragment extends Fragment implements CallbackHandler.EventListener {
public static final CallbackHandler.Event repoLoadDone = new CallbackHandler.Event();
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyTv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
private List<Repo> mUpdateRepos = new ArrayList<>();
private List<Repo> mInstalledRepos = new ArrayList<>();
private List<Repo> mOthersRepos = new ArrayList<>();
private List<Repo> fUpdateRepos = new ArrayList<>();
private List<Repo> fInstalledRepos = new ArrayList<>();
private List<Repo> fOthersRepos = new ArrayList<>();
private SimpleSectionedRecyclerViewAdapter mSectionedAdapter;
private SearchView.OnQueryTextListener searchListener;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.repos_fragment, container, false);
ButterKnife.bind(this, view);
mSectionedAdapter = new
SimpleSectionedRecyclerViewAdapter(getActivity(), R.layout.section,
R.id.section_text, new ReposAdapter(fUpdateRepos, fInstalledRepos, fOthersRepos));
recyclerView.setAdapter(mSectionedAdapter);
mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.GONE);
new Async.LoadRepos(getActivity()).exec();
});
if (repoLoadDone.isTriggered) {
reloadRepos();
updateUI();
}
searchListener = new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
new FilterApps().exec(newText);
return false;
}
};
return view;
}
@Override
public void onTrigger(CallbackHandler.Event event) {
Logger.dev("ReposFragment: UI refresh triggered");
reloadRepos();
updateUI();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_repo, menu);
SearchView search = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.repo_search));
search.setOnQueryTextListener(searchListener);
}
@Override
public void onResume() {
super.onResume();
setHasOptionsMenu(true);
CallbackHandler.register(repoLoadDone, this);
getActivity().setTitle(R.string.downloads);
}
@Override
public void onDestroy() {
super.onDestroy();
CallbackHandler.unRegister(repoLoadDone, this);
}
private void reloadRepos() {
ModuleHelper.getRepoLists(mUpdateRepos, mInstalledRepos, mOthersRepos);
fUpdateRepos.clear();
fInstalledRepos.clear();
fOthersRepos.clear();
fUpdateRepos.addAll(mUpdateRepos);
fInstalledRepos.addAll(mInstalledRepos);
fOthersRepos.addAll(mOthersRepos);
}
private void updateUI() {
if (fUpdateRepos.size() + fInstalledRepos.size() + fOthersRepos.size() == 0) {
emptyTv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
List<SimpleSectionedRecyclerViewAdapter.Section> sections = new ArrayList<>();
if (!fUpdateRepos.isEmpty()) {
sections.add(new SimpleSectionedRecyclerViewAdapter.Section(0, getString(R.string.update_available)));
}
if (!fInstalledRepos.isEmpty()) {
sections.add(new SimpleSectionedRecyclerViewAdapter.Section(fUpdateRepos.size(), getString(R.string.installed)));
}
if (!fOthersRepos.isEmpty()) {
sections.add(new SimpleSectionedRecyclerViewAdapter.Section(fUpdateRepos.size() + fInstalledRepos.size(), getString(R.string.not_installed)));
}
SimpleSectionedRecyclerViewAdapter.Section[] array = sections.toArray(new SimpleSectionedRecyclerViewAdapter.Section[sections.size()]);
mSectionedAdapter.setSections(array);
emptyTv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
mSwipeRefreshLayout.setRefreshing(false);
}
private class FilterApps extends Async.NormalTask<String, Void, Void> {
@Override
protected Void doInBackground(String... strings) {
String newText = strings[0];
fUpdateRepos.clear();
fInstalledRepos.clear();
fOthersRepos.clear();
for (Repo repo: mUpdateRepos) {
if (repo.getName().toLowerCase().contains(newText.toLowerCase())
|| repo.getAuthor().toLowerCase().contains(newText.toLowerCase())
|| repo.getDescription().toLowerCase().contains(newText.toLowerCase())
) {
fUpdateRepos.add(repo);
}
}
for (Repo repo: mInstalledRepos) {
if (repo.getName().toLowerCase().contains(newText.toLowerCase())
|| repo.getAuthor().toLowerCase().contains(newText.toLowerCase())
|| repo.getDescription().toLowerCase().contains(newText.toLowerCase())
) {
fInstalledRepos.add(repo);
}
}
for (Repo repo: mOthersRepos) {
if (repo.getName().toLowerCase().contains(newText.toLowerCase())
|| repo.getAuthor().toLowerCase().contains(newText.toLowerCase())
|| repo.getDescription().toLowerCase().contains(newText.toLowerCase())
) {
fOthersRepos.add(repo);
}
}
return null;
}
@Override
protected void onPostExecute(Void v) {
updateUI();
}
}
}

View File

@@ -1,213 +0,0 @@
package com.topjohnwu.magisk;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.WindowManager;
import android.widget.Toast;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.ModuleHelper;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import butterknife.BindView;
import butterknife.ButterKnife;
public class SettingsActivity extends AppCompatActivity {
@BindView(R.id.toolbar) Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
Logger.dev("AboutActivity: Theme is " + theme);
if (Utils.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
setContentView(R.layout.activity_container);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(R.string.settings);
ab.setDisplayHomeAsUpEnabled(true);
}
setFloating();
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().add(R.id.container, new SettingsFragment()).commit();
}
}
public void setFloating() {
boolean isTablet = getResources().getBoolean(R.bool.isTablet);
if (isTablet) {
WindowManager.LayoutParams params = getWindow().getAttributes();
params.height = getResources().getDimensionPixelSize(R.dimen.floating_height);
params.width = getResources().getDimensionPixelSize(R.dimen.floating_width);
params.alpha = 1.0f;
params.dimAmount = 0.6f;
params.flags |= 2;
getWindow().setAttributes(params);
setFinishOnTouchOutside(true);
}
}
public static class SettingsFragment extends PreferenceFragment
implements SharedPreferences.OnSharedPreferenceChangeListener {
private ListPreference themePreference;
private SharedPreferences prefs;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_settings);
PreferenceManager.setDefaultValues(getActivity(), R.xml.app_settings, false);
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
themePreference = (ListPreference) findPreference("theme");
CheckBoxPreference busyboxPreference = (CheckBoxPreference) findPreference("busybox");
CheckBoxPreference magiskhidePreference = (CheckBoxPreference) findPreference("magiskhide");
CheckBoxPreference hostsPreference = (CheckBoxPreference) findPreference("hosts");
Preference clear = findPreference("clear");
clear.setOnPreferenceClickListener((pref) -> {
SharedPreferences repoMap = getActivity().getSharedPreferences(ModuleHelper.FILE_KEY, Context.MODE_PRIVATE);
repoMap.edit()
.putString(ModuleHelper.ETAG_KEY, "")
.putInt(ModuleHelper.VERSION_KEY, 0)
.apply();
new Async.LoadRepos(getActivity()).exec();
Toast.makeText(getActivity(), R.string.repo_cache_cleared, Toast.LENGTH_LONG).show();
return true;
});
if (Utils.isDarkTheme) {
themePreference.setSummary(R.string.theme_dark);
} else {
themePreference.setSummary(R.string.theme_default);
}
if (StatusFragment.magiskVersion < 9) {
hostsPreference.setEnabled(false);
busyboxPreference.setEnabled(false);
} else if (StatusFragment.magiskVersion < 8) {
magiskhidePreference.setEnabled(false);
} else if (! Shell.rootAccess()) {
busyboxPreference.setEnabled(false);
magiskhidePreference.setEnabled(false);
hostsPreference.setEnabled(false);
} else {
busyboxPreference.setEnabled(true);
magiskhidePreference.setEnabled(true);
hostsPreference.setEnabled(true);
}
}
@Override
public void onResume() {
super.onResume();
prefs.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
prefs.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
Logger.dev("Settings: Prefs change " + key);
boolean checked;
switch (key) {
case "theme":
String theme = prefs.getString("theme", getString(R.string.theme_default_value));
if (Utils.isDarkTheme != theme.equalsIgnoreCase(getString(R.string.theme_dark_value))) {
Utils.isDarkTheme = !Utils.isDarkTheme;
getActivity().recreate();
MainActivity.recreate.trigger();
}
break;
case "magiskhide":
checked = prefs.getBoolean("magiskhide", false);
new Async.RootTask<Void, Void, Void>() {
private boolean enable = checked;
@Override
protected Void doInBackground(Void... params) {
if (enable) {
Utils.createFile("/magisk/.core/magiskhide/enable");
} else {
Utils.removeItem("/magisk/.core/magiskhide/enable");
}
return null;
}
}.exec();
Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
break;
case "busybox":
checked = prefs.getBoolean("busybox", false);
new Async.RootTask<Void, Void, Void>() {
private boolean enable = checked;
@Override
protected Void doInBackground(Void... params) {
if (enable) {
Utils.createFile("/magisk/.core/busybox/enable");
} else {
Utils.removeItem("/magisk/.core/busybox/enable");
}
return null;
}
}.exec();
Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
break;
case "hosts":
checked = prefs.getBoolean("hosts", false);
new Async.RootTask<Void, Void, Void>() {
private boolean enable = checked;
@Override
protected Void doInBackground(Void... voids) {
if (enable) {
Shell.su("cp -af /system/etc/hosts /magisk/.core/hosts",
"mount -o bind /magisk/.core/hosts /system/etc/hosts");
} else {
Shell.su("umount -l /system/etc/hosts",
"rm -f /magisk/.core/hosts");
}
return null;
}
}.exec();
break;
case "developer_logging":
Logger.devLog = prefs.getBoolean("developer_logging", false);
break;
case "shell_logging":
Logger.logShell = prefs.getBoolean("shell_logging", false);
break;
}
}
}
}

View File

@@ -1,55 +0,0 @@
package com.topjohnwu.magisk;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
public class SplashActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplication());
String theme = prefs.getString("theme", getString(R.string.theme_default_value));
Utils.isDarkTheme = theme.equalsIgnoreCase(getString(R.string.theme_dark_value));
if (Utils.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
Logger.devLog = prefs.getBoolean("developer_logging", false);
Logger.logShell = prefs.getBoolean("shell_logging", false);
// Initialize prefs
prefs.edit()
.putBoolean("magiskhide", Utils.itemExist(false, "/magisk/.core/magiskhide/enable"))
.putBoolean("busybox", Utils.commandExists("busybox"))
.putBoolean("hosts", Utils.itemExist(false, "/magisk/.core/hosts"))
.apply();
// Start all async tasks
new Async.GetBootBlocks().exec();
new Async.CheckUpdates().exec();
new Async.LoadModules() {
@Override
protected void onPostExecute(Void v) {
super.onPostExecute(v);
new Async.LoadRepos(getApplicationContext()).exec();
}
}.exec();
new Async.LoadApps(getPackageManager()).exec();
// Start main activity
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
}

View File

@@ -1,306 +0,0 @@
package com.topjohnwu.magisk;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import java.util.List;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.ButterKnife;
public class StatusFragment extends Fragment implements CallbackHandler.EventListener {
public static double magiskVersion, remoteMagiskVersion = -1;
public static String magiskVersionString = "(none)", magiskLink, releaseNoteLink;
public static int SNCheckResult = -1;
private static boolean noDialog = false;
public static final CallbackHandler.Event updateCheckDone = new CallbackHandler.Event();
public static final CallbackHandler.Event safetyNetDone = new CallbackHandler.Event();
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.magisk_status_container) View magiskStatusContainer;
@BindView(R.id.magisk_status_icon) ImageView magiskStatusIcon;
@BindView(R.id.magisk_version) TextView magiskVersionText;
@BindView(R.id.magisk_update_status) TextView magiskUpdateText;
@BindView(R.id.magisk_check_updates_progress) ProgressBar magiskCheckUpdatesProgress;
@BindView(R.id.root_status_container) View rootStatusContainer;
@BindView(R.id.root_status_icon) ImageView rootStatusIcon;
@BindView(R.id.root_status) TextView rootStatusText;
@BindView(R.id.root_info) TextView rootInfoText;
@BindView(R.id.safetyNet_container) View safetyNetContainer;
@BindView(R.id.safetyNet_icon) ImageView safetyNetIcon;
@BindView(R.id.safetyNet_status) TextView safetyNetStatusText;
@BindView(R.id.safetyNet_check_progress) ProgressBar safetyNetProgress;
@BindColor(R.color.red500) int colorBad;
@BindColor(R.color.green500) int colorOK;
@BindColor(R.color.yellow500) int colorWarn;
@BindColor(R.color.grey500) int colorNeutral;
@BindColor(R.color.blue500) int colorInfo;
@BindColor(android.R.color.transparent) int trans;
int defaultColor;
static {
checkMagiskInfo();
}
private AlertDialog updateMagisk;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.status_fragment, container, false);
ButterKnife.bind(this, v);
defaultColor = magiskUpdateText.getCurrentTextColor();
mSwipeRefreshLayout.setOnRefreshListener(() -> {
magiskStatusContainer.setBackgroundColor(trans);
magiskStatusIcon.setImageResource(0);
magiskUpdateText.setText(R.string.checking_for_updates);
magiskCheckUpdatesProgress.setVisibility(View.VISIBLE);
magiskUpdateText.setTextColor(defaultColor);
safetyNetProgress.setVisibility(View.GONE);
safetyNetContainer.setBackgroundColor(colorNeutral);
safetyNetIcon.setImageResource(R.drawable.ic_safetynet);
safetyNetStatusText.setText(R.string.safetyNet_check_text);
safetyNetStatusText.setTextColor(defaultColor);
safetyNetDone.isTriggered = false;
noDialog = false;
updateUI();
new Async.CheckUpdates().exec();
});
safetyNetContainer.setOnClickListener(view -> {
safetyNetProgress.setVisibility(View.VISIBLE);
safetyNetContainer.setBackgroundColor(trans);
safetyNetIcon.setImageResource(0);
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
Async.checkSafetyNet(getActivity());
});
if (magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
noDialog = true;
Utils.getAlertDialogBuilder(getActivity())
.setTitle(R.string.no_magisk_title)
.setMessage(R.string.no_magisk_msg)
.setCancelable(true)
.setPositiveButton(R.string.goto_install, (dialogInterface, i) -> {
((MainActivity) getActivity()).navigationView.setCheckedItem(R.id.install);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
try {
transaction.replace(R.id.content_frame, new InstallFragment(), "install").commit();
} catch (IllegalStateException ignored) {}
})
.setNegativeButton(R.string.no_thanks, null)
.show();
}
updateUI();
return v;
}
@Override
public void onTrigger(CallbackHandler.Event event) {
if (event == updateCheckDone) {
Logger.dev("StatusFragment: Update Check UI refresh triggered");
updateCheckUI();
} else if (event == safetyNetDone) {
Logger.dev("StatusFragment: SafetyNet UI refresh triggered");
updateSafetyNetUI();
}
}
@Override
public void onResume() {
super.onResume();
CallbackHandler.register(updateCheckDone, this);
CallbackHandler.register(safetyNetDone, this);
if (updateCheckDone.isTriggered) {
updateCheckUI();
}
if (safetyNetDone.isTriggered) {
updateSafetyNetUI();
}
getActivity().setTitle(R.string.status);
}
@Override
public void onPause() {
super.onPause();
CallbackHandler.unRegister(updateCheckDone, this);
CallbackHandler.unRegister(safetyNetDone, this);
}
private static void checkMagiskInfo() {
List<String> ret = Shell.sh("getprop magisk.version");
if (ret.get(0).length() == 0) {
magiskVersion = -1;
} else {
try {
magiskVersionString = ret.get(0);
magiskVersion = Double.parseDouble(ret.get(0));
} catch (NumberFormatException e) {
// Custom version don't need to receive updates
magiskVersion = Double.POSITIVE_INFINITY;
}
}
}
private void updateUI() {
int image, color;
checkMagiskInfo();
if (magiskVersion < 0) {
magiskVersionText.setText(R.string.magisk_version_error);
} else {
magiskVersionText.setText(getString(R.string.magisk_version, magiskVersionString));
}
if (Shell.rootStatus == 1) {
color = colorOK;
image = R.drawable.ic_check_circle;
rootStatusText.setText(R.string.proper_root);
rootInfoText.setText(Shell.sh("su -v").get(0));
} else {
rootInfoText.setText(R.string.root_info_warning);
if (Shell.rootStatus == 0) {
color = colorBad;
image = R.drawable.ic_cancel;
rootStatusText.setText(R.string.not_rooted);
} else {
color = colorNeutral;
image = R.drawable.ic_help;
rootStatusText.setText(R.string.root_error);
}
}
rootStatusContainer.setBackgroundColor(color);
rootStatusText.setTextColor(color);
rootInfoText.setTextColor(color);
rootStatusIcon.setImageResource(image);
}
private void updateCheckUI() {
int image, color;
if (remoteMagiskVersion < 0) {
color = colorNeutral;
image = R.drawable.ic_help;
magiskUpdateText.setText(R.string.cannot_check_updates);
} else if (remoteMagiskVersion > magiskVersion) {
color = colorInfo;
image = R.drawable.ic_update;
magiskUpdateText.setText(getString(R.string.magisk_update_available, remoteMagiskVersion));
} else {
color = colorOK;
image = R.drawable.ic_check_circle;
magiskUpdateText.setText(getString(R.string.up_to_date, getString(R.string.magisk)));
}
if (magiskVersion < 0) {
color = colorBad;
image = R.drawable.ic_cancel;
}
magiskStatusContainer.setBackgroundColor(color);
magiskVersionText.setTextColor(color);
magiskUpdateText.setTextColor(color);
magiskStatusIcon.setImageResource(image);
magiskCheckUpdatesProgress.setVisibility(View.GONE);
mSwipeRefreshLayout.setRefreshing(false);
updateMagisk = Utils.getAlertDialogBuilder(getActivity())
.setTitle(R.string.magisk_update_title)
.setMessage(getString(R.string.magisk_update_message, remoteMagiskVersion))
.setCancelable(true)
.setPositiveButton(R.string.goto_install, (dialogInterface, i) -> {
((MainActivity) getActivity()).navigationView.setCheckedItem(R.id.install);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
try {
transaction.replace(R.id.content_frame, new InstallFragment(), "install").commit();
} catch (IllegalStateException ignored) {}
})
.setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(releaseNoteLink)));
})
.setNegativeButton(R.string.no_thanks, null)
.create();
if (magiskVersion < remoteMagiskVersion && Shell.rootAccess()) {
magiskStatusContainer.setOnClickListener(view -> updateMagisk.show());
if (!noDialog) {
noDialog = true;
updateMagisk.show();
}
}
}
private void updateSafetyNetUI() {
int image, color;
safetyNetProgress.setVisibility(View.GONE);
switch (SNCheckResult) {
case -3:
color = colorNeutral;
image = R.drawable.ic_help;
safetyNetStatusText.setText(R.string.safetyNet_connection_suspended);
break;
case -2:
color = colorNeutral;
image = R.drawable.ic_help;
safetyNetStatusText.setText(R.string.safetyNet_connection_failed);
break;
case -1:
color = colorNeutral;
image = R.drawable.ic_help;
safetyNetStatusText.setText(R.string.safetyNet_error);
break;
case 0:
color = colorBad;
image = R.drawable.ic_cancel;
safetyNetStatusText.setText(R.string.safetyNet_fail);
break;
case 1:
default:
color = colorOK;
image = R.drawable.ic_check_circle;
safetyNetStatusText.setText(R.string.safetyNet_pass);
break;
}
safetyNetContainer.setBackgroundColor(color);
safetyNetStatusText.setTextColor(color);
safetyNetIcon.setImageResource(image);
}
}

View File

@@ -1,128 +0,0 @@
package com.topjohnwu.magisk.adapters;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.Filter;
import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
private List<ApplicationInfo> mOriginalList, mList;
private List<String> mHideList;
private PackageManager packageManager;
private ApplicationFilter filter;
public ApplicationAdapter(PackageManager packageManager) {
mOriginalList = mList = Collections.emptyList();
mHideList = Collections.emptyList();
this.packageManager = packageManager;
}
public void setLists(List<ApplicationInfo> listApps, List<String> hideList) {
mOriginalList = mList = Collections.unmodifiableList(listApps);
mHideList = new ArrayList<>(hideList);
notifyDataSetChanged();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
ButterKnife.bind(this, mView);
return new ViewHolder(mView);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
ApplicationInfo info = mList.get(position);
holder.appIcon.setImageDrawable(info.loadIcon(packageManager));
holder.appName.setText(info.loadLabel(packageManager));
holder.appPackage.setText(info.packageName);
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(mHideList.contains(info.packageName));
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked) {
new Async.MagiskHide().add(info.packageName);
mHideList.add(info.packageName);
} else {
new Async.MagiskHide().rm(info.packageName);
mHideList.remove(info.packageName);
}
});
}
@Override
public int getItemCount() {
return mList.size();
}
public void filter(String constraint) {
if (filter == null) {
filter = new ApplicationFilter();
}
filter.filter(constraint);
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.app_package) TextView appPackage;
@BindView(R.id.checkbox) CheckBox checkBox;
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
private class ApplicationFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<ApplicationInfo> filteredApps;
if (constraint == null || constraint.length() == 0) {
filteredApps = mOriginalList;
} else {
filteredApps = new ArrayList<>();
String filter = constraint.toString().toLowerCase();
for (ApplicationInfo info : mOriginalList) {
if (Utils.lowercaseContains(info.loadLabel(packageManager), filter)
|| Utils.lowercaseContains(info.packageName, filter)) {
filteredApps.add(info);
}
}
}
FilterResults results = new FilterResults();
results.values = filteredApps;
results.count = filteredApps.size();
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mList = (List<ApplicationInfo>) results.values;
notifyDataSetChanged();
}
}
}

View File

@@ -1,221 +0,0 @@
package com.topjohnwu.magisk.adapters;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.receivers.RepoDlReceiver;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebWindow;
import java.util.HashSet;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder> {
private List<Repo> mUpdateRepos, mInstalledRepos, mOthersRepos;
private HashSet<Repo> expandList = new HashSet<>();
public ReposAdapter(List<Repo> update, List<Repo> installed, List<Repo> others) {
mUpdateRepos = update;
mInstalledRepos = installed;
mOthersRepos = others;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_repo, parent, false);
ButterKnife.bind(this, v);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
Context context = holder.itemView.getContext();
Repo repo = getItem(position);
holder.title.setText(repo.getName());
holder.versionName.setText(repo.getVersion());
String author = repo.getAuthor();
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.description.setText(repo.getDescription());
holder.setExpanded(expandList.contains(repo));
holder.itemView.setOnClickListener(view -> {
if (holder.mExpanded) {
holder.collapse();
expandList.remove(repo);
} else {
holder.expand();
expandList.add(repo);
}
});
holder.changeLog.setOnClickListener(view -> {
if (!TextUtils.isEmpty(repo.getLogUrl())) {
new WebWindow(context.getString(R.string.changelog), repo.getLogUrl(), context);
}
});
holder.updateImage.setOnClickListener(view -> {
String filename = repo.getName() + "-" + repo.getVersion() + ".zip";
Utils.getAlertDialogBuilder(context)
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
.setMessage(context.getString(R.string.repo_install_msg, filename))
.setCancelable(true)
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
context,
new RepoDlReceiver(),
repo.getZipUrl(),
Utils.getLegalFilename(filename)))
.setNegativeButton(R.string.no_thanks, null)
.show();
});
holder.authorLink.setOnClickListener(view -> {
if (!TextUtils.isEmpty(repo.getDonateUrl())) {
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getDonateUrl())));
}
});
holder.supportLink.setOnClickListener(view -> {
if (!TextUtils.isEmpty(repo.getSupportUrl())) {
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getSupportUrl())));
}
});
}
@Override
public int getItemCount() {
return mUpdateRepos.size() + mInstalledRepos.size() + mOthersRepos.size();
}
private Repo getItem(int position) {
if (position >= mUpdateRepos.size()) {
position -= mUpdateRepos.size();
if (position >= mInstalledRepos.size()) {
position -= mInstalledRepos.size();
return mOthersRepos.get(position);
} else {
return mInstalledRepos.get(position);
}
} else {
return mUpdateRepos.get(position);
}
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.title) TextView title;
@BindView(R.id.version_name) TextView versionName;
@BindView(R.id.description) TextView description;
@BindView(R.id.author) TextView author;
@BindView(R.id.expand_layout) LinearLayout expandLayout;
@BindView(R.id.update) ImageView updateImage;
@BindView(R.id.changeLog) ImageView changeLog;
@BindView(R.id.authorLink) ImageView authorLink;
@BindView(R.id.supportLink) ImageView supportLink;
private ValueAnimator mAnimator;
private ObjectAnimator animY2;
private boolean mExpanded = false;
private static int expandHeight = 0;
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
expandLayout.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (expandHeight == 0) {
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
expandLayout.measure(widthSpec, heightSpec);
expandHeight = expandLayout.getMeasuredHeight();
}
expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
expandLayout.setVisibility(View.GONE);
mAnimator = slideAnimator(0, expandHeight);
animY2 = ObjectAnimator.ofFloat(updateImage, "translationY", expandHeight / 2);
return true;
}
});
}
private void setExpanded(boolean expanded) {
mExpanded = expanded;
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = expanded ? expandHeight : 0;
expandLayout.setLayoutParams(layoutParams);
expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE);
if (expanded) {
updateImage.setTranslationY(expandHeight / 2);
} else {
updateImage.setTranslationY(0);
}
}
private void expand() {
expandLayout.setVisibility(View.VISIBLE);
mAnimator.start();
animY2.start();
mExpanded = true;
}
private void collapse() {
if (!mExpanded) return;
int finalHeight = expandLayout.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
expandLayout.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {}
@Override
public void onAnimationCancel(Animator animator) {}
@Override
public void onAnimationRepeat(Animator animator) {}
});
mAnimator.start();
animY2.reverse();
mExpanded = false;
}
private ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(valueAnimator -> {
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = value;
expandLayout.setLayoutParams(layoutParams);
});
return animator;
}
}
}

View File

@@ -1,183 +0,0 @@
package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.Arrays;
import java.util.Comparator;
public class SimpleSectionedRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context mContext;
private static final int SECTION_TYPE = 0;
private boolean mValid = true;
private int mSectionResourceId;
private int mTextResourceId;
private LayoutInflater mLayoutInflater;
private RecyclerView.Adapter mBaseAdapter;
private SparseArray<Section> mSections = new SparseArray<Section>();
public SimpleSectionedRecyclerViewAdapter(Context context, int sectionResourceId, int textResourceId,
RecyclerView.Adapter baseAdapter) {
mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSectionResourceId = sectionResourceId;
mTextResourceId = textResourceId;
mBaseAdapter = baseAdapter;
mContext = context;
mBaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
mValid = mBaseAdapter.getItemCount()>0;
notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
mValid = mBaseAdapter.getItemCount()>0;
notifyItemRangeChanged(positionStart, itemCount);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
mValid = mBaseAdapter.getItemCount()>0;
notifyItemRangeInserted(positionStart, itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
mValid = mBaseAdapter.getItemCount()>0;
notifyItemRangeRemoved(positionStart, itemCount);
}
});
}
public static class SectionViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public SectionViewHolder(View view, int mTextResourceid) {
super(view);
title = (TextView) view.findViewById(mTextResourceid);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) {
if (typeView == SECTION_TYPE) {
final View view = LayoutInflater.from(mContext).inflate(mSectionResourceId, parent, false);
return new SectionViewHolder(view,mTextResourceId);
}else{
return mBaseAdapter.onCreateViewHolder(parent, typeView -1);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder sectionViewHolder, int position) {
if (isSectionHeaderPosition(position)) {
((SectionViewHolder)sectionViewHolder).title.setText(mSections.get(position).title);
}else{
mBaseAdapter.onBindViewHolder(sectionViewHolder,sectionedPositionToPosition(position));
}
}
@Override
public int getItemViewType(int position) {
return isSectionHeaderPosition(position)
? SECTION_TYPE
: mBaseAdapter.getItemViewType(sectionedPositionToPosition(position)) +1 ;
}
public static class Section {
int firstPosition;
int sectionedPosition;
CharSequence title;
public Section(int firstPosition, CharSequence title) {
this.firstPosition = firstPosition;
this.title = title;
}
public CharSequence getTitle() {
return title;
}
}
public void setSections(Section[] sections) {
mSections.clear();
Arrays.sort(sections, new Comparator<Section>() {
@Override
public int compare(Section o, Section o1) {
return (o.firstPosition == o1.firstPosition)
? 0
: ((o.firstPosition < o1.firstPosition) ? -1 : 1);
}
});
int offset = 0; // offset positions for the headers we're adding
for (Section section : sections) {
section.sectionedPosition = section.firstPosition + offset;
mSections.append(section.sectionedPosition, section);
++offset;
}
notifyDataSetChanged();
}
public int positionToSectionedPosition(int position) {
int offset = 0;
for (int i = 0; i < mSections.size(); i++) {
if (mSections.valueAt(i).firstPosition > position) {
break;
}
++offset;
}
return position + offset;
}
public int sectionedPositionToPosition(int sectionedPosition) {
if (isSectionHeaderPosition(sectionedPosition)) {
return RecyclerView.NO_POSITION;
}
int offset = 0;
for (int i = 0; i < mSections.size(); i++) {
if (mSections.valueAt(i).sectionedPosition > sectionedPosition) {
break;
}
--offset;
}
return sectionedPosition + offset;
}
public boolean isSectionHeaderPosition(int position) {
return mSections.get(position) != null;
}
@Override
public long getItemId(int position) {
return isSectionHeaderPosition(position)
? Integer.MAX_VALUE - mSections.indexOfKey(position)
: mBaseAdapter.getItemId(sectionedPositionToPosition(position));
}
@Override
public int getItemCount() {
return (mValid ? mBaseAdapter.getItemCount() + mSections.size() : 0);
}
}

View File

@@ -1,108 +0,0 @@
package com.topjohnwu.magisk.module;
import android.support.annotation.NonNull;
import com.topjohnwu.magisk.utils.Logger;
import java.util.List;
public abstract class BaseModule implements Comparable<BaseModule> {
protected String mId, mName, mVersion, mAuthor, mDescription, mSupportUrl, mDonateUrl;
protected boolean mIsCacheModule = false;
protected int mVersionCode = 0;
protected void parseProps(List<String> props) throws CacheModException { parseProps(props.toArray(new String[props.size()])); }
protected void parseProps(String[] props) throws CacheModException {
for (String line : props) {
String[] prop = line.split("=", 2);
if (prop.length != 2) {
continue;
}
String key = prop[0].trim();
if (key.charAt(0) == '#') {
continue;
}
switch (key) {
case "id":
this.mId = prop[1];
break;
case "name":
this.mName = prop[1];
break;
case "version":
this.mVersion = prop[1];
break;
case "versionCode":
try {
this.mVersionCode = Integer.parseInt(prop[1]);
} catch (NumberFormatException ignored) {}
break;
case "author":
this.mAuthor = prop[1];
break;
case "description":
this.mDescription = prop[1];
break;
case "support":
this.mSupportUrl = prop[1];
break;
case "donate":
this.mDonateUrl = prop[1];
break;
case "cacheModule":
this.mIsCacheModule = Boolean.parseBoolean(prop[1]);
break;
default:
break;
}
}
if (mIsCacheModule)
throw new CacheModException(mId);
}
public String getName() {
return mName;
}
public String getVersion() {
return mVersion;
}
public String getAuthor() {
return mAuthor;
}
public String getId() {return mId; }
public String getDescription() {
return mDescription;
}
public int getVersionCode() {
return mVersionCode;
}
public String getDonateUrl() {
return mDonateUrl;
}
public String getSupportUrl() {
return mSupportUrl;
}
public static class CacheModException extends Exception {
public CacheModException(String id) {
Logger.dev("Cache mods are no longer supported! id: " + id);
}
}
@Override
public int compareTo(@NonNull BaseModule o) {
return this.getName().toLowerCase().compareTo(o.getName().toLowerCase());
}
}

View File

@@ -1,62 +0,0 @@
package com.topjohnwu.magisk.module;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
public class Module extends BaseModule {
private String mRemoveFile, mDisableFile, mUpdateFile;
private boolean mEnable, mRemove, mUpdated;
public Module(String path) throws CacheModException {
parseProps(Utils.readFile(path + "/module.prop"));
mRemoveFile = path + "/remove";
mDisableFile = path + "/disable";
mUpdateFile = path + "/update";
if (mId == null) {
int sep = path.lastIndexOf('/');
mId = path.substring(sep + 1);
}
if (mName == null)
mName = mId;
Logger.dev("Creating Module, id: " + mId);
mEnable = !Utils.itemExist(mDisableFile);
mRemove = Utils.itemExist(mRemoveFile);
mUpdated = Utils.itemExist(mUpdateFile);
}
public void createDisableFile() {
mEnable = !Utils.createFile(mDisableFile);
}
public void removeDisableFile() {
mEnable = Utils.removeItem(mDisableFile);
}
public boolean isEnabled() {
return mEnable;
}
public void createRemoveFile() {
mRemove = Utils.createFile(mRemoveFile);
}
public void deleteRemoveFile() {
mRemove = !Utils.removeItem(mRemoveFile);
}
public boolean willBeRemoved() {
return mRemove;
}
public boolean isUpdated() {
return mUpdated;
}
}

View File

@@ -1,56 +0,0 @@
package com.topjohnwu.magisk.module;
import android.content.Context;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.WebService;
import java.util.Date;
public class Repo extends BaseModule {
private String mLogUrl, mManifestUrl, mZipUrl;
private Date mLastUpdate;
public Repo(Context context, String name, Date lastUpdate) throws CacheModException {
mLastUpdate = lastUpdate;
mLogUrl = context.getString(R.string.file_url, name, "changelog.txt");
mManifestUrl = context.getString(R.string.file_url, name, "module.prop");
mZipUrl = context.getString(R.string.zip_url, name);
update();
}
public void update() throws CacheModException {
Logger.dev("Repo: Re-fetch prop");
String props = WebService.request(mManifestUrl, WebService.GET, true);
String lines[] = props.split("\\n");
parseProps(lines);
}
public void update(Date lastUpdate) throws CacheModException {
Logger.dev("Repo: Old: " + mLastUpdate);
Logger.dev("Repo: New: " + lastUpdate);
if (mIsCacheModule)
throw new CacheModException(mId);
if (lastUpdate.after(mLastUpdate)) {
mLastUpdate = lastUpdate;
update();
}
}
public String getZipUrl() {
return mZipUrl;
}
public String getLogUrl() {
return mLogUrl;
}
public String getManifestUrl() {
return mManifestUrl;
}
public Date getLastUpdate() {
return mLastUpdate;
}
}

View File

@@ -1,67 +0,0 @@
package com.topjohnwu.magisk.receivers;
import android.net.Uri;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.StatusFragment;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.File;
public class MagiskDlReceiver extends DownloadReceiver {
String mBoot;
boolean mEnc, mVerity;
public MagiskDlReceiver(String bootImage, boolean keepEnc, boolean keepVerity) {
mBoot = bootImage;
mEnc = keepEnc;
mVerity = keepVerity;
}
@Override
public void onDownloadDone(Uri uri) {
new Async.FlashZIP(mContext, uri, mFilename) {
@Override
protected void preProcessing() throws Throwable {
Shell.su(
"echo \"BOOTIMAGE=/dev/block/" + mBoot + "\" > /dev/.magisk",
"echo \"KEEPFORCEENCRYPT=" + String.valueOf(mEnc) + "\" >> /dev/.magisk",
"echo \"KEEPVERITY=" + String.valueOf(mVerity) + "\" >> /dev/.magisk"
);
}
@Override
protected boolean unzipAndCheck() {
publishProgress(mContext.getString(R.string.zip_install_unzip_zip_msg));
if (Shell.rootAccess()) {
// We might not have busybox yet, unzip with Java
// We will have complete busybox after Magisk installation
ZipUtils.unzip(mCachedFile, new File(mCachedFile.getParent(), "magisk"));
Shell.su(
"mkdir -p " + Async.TMP_FOLDER_PATH + "/magisk",
"cp -af " + mCachedFile.getParent() + "/magisk/. " + Async.TMP_FOLDER_PATH + "/magisk",
"mv -f " + mCachedFile.getParent() + "/magisk/META-INF " + mCachedFile.getParent() + "/META-INF"
);
}
return true;
}
@Override
protected void onSuccess() {
new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
Shell.su("setprop magisk.version "
+ String.valueOf(StatusFragment.remoteMagiskVersion));
return null;
}
}.exec();
super.onSuccess();
}
}.exec();
}
}

View File

@@ -1,42 +0,0 @@
package com.topjohnwu.magisk.receivers;
import android.net.Uri;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Utils.ByteArrayInOutStream;
import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.OutputStream;
public class RepoDlReceiver extends DownloadReceiver {
@Override
public void onDownloadDone(Uri uri) {
// Flash the zip
new Async.FlashZIP(mContext, uri, mFilename){
@Override
protected void preProcessing() throws Throwable {
// Process and sign the zip
publishProgress(mContext.getString(R.string.zip_install_process_zip_msg));
ByteArrayInOutStream buffer = new ByteArrayInOutStream();
// First remove top folder (the folder with the repo name) in Github source zip
ZipUtils.removeTopFolder(mContext.getContentResolver().openInputStream(mUri), buffer);
// Then sign the zip for the first time
ZipUtils.signZip(mContext, buffer.getInputStream(), buffer, false);
// Adjust the zip to prevent unzip issues
ZipUtils.adjustZip(buffer);
// Finally, sign the whole zip file again
ZipUtils.signZip(mContext, buffer.getInputStream(), buffer, true);
// Write it back to the downloaded zip
OutputStream out = mContext.getContentResolver().openOutputStream(mUri);
buffer.writeTo(out);
out.close();
}
}.exec();
}
}

View File

@@ -1,340 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.OpenableColumns;
import android.util.Log;
import android.widget.Toast;
import com.topjohnwu.magisk.InstallFragment;
import com.topjohnwu.magisk.MagiskHideFragment;
import com.topjohnwu.magisk.ModulesFragment;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ReposFragment;
import com.topjohnwu.magisk.StatusFragment;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class Async {
public abstract static class RootTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
@SafeVarargs
public final void exec(Params... params) {
if (!Shell.rootAccess()) return;
executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, params);
}
}
public abstract static class NormalTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
@SafeVarargs
public final void exec(Params... params) {
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
}
public static final String UPDATE_JSON = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/updates/magisk_update.json";
public static final String MAGISK_HIDE_PATH = "/magisk/.core/magiskhide/";
public static final String TMP_FOLDER_PATH = "/dev/tmp";
public static class CheckUpdates extends NormalTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
String jsonStr = WebService.request(UPDATE_JSON, WebService.GET);
try {
JSONObject json = new JSONObject(jsonStr);
JSONObject magisk = json.getJSONObject("magisk");
StatusFragment.remoteMagiskVersion = magisk.getDouble("versionCode");
StatusFragment.magiskLink = magisk.getString("link");
StatusFragment.releaseNoteLink = magisk.getString("note");
} catch (JSONException ignored) {}
return null;
}
@Override
protected void onPostExecute(Void v) {
StatusFragment.updateCheckDone.trigger();
}
}
public static void checkSafetyNet(Context context) {
new SafetyNetHelper(context) {
@Override
public void handleResults(int i) {
StatusFragment.SNCheckResult = i;
StatusFragment.safetyNetDone.trigger();
}
}.requestTest();
}
public static class LoadModules extends RootTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
ModuleHelper.createModuleMap();
return null;
}
@Override
protected void onPostExecute(Void v) {
ModulesFragment.moduleLoadDone.trigger();
}
}
public static class LoadRepos extends NormalTask<Void, Void, Void> {
private Context mContext;
public LoadRepos(Context context) {
mContext = context;
}
@Override
protected Void doInBackground(Void... voids) {
ModuleHelper.createRepoMap(mContext);
return null;
}
@Override
protected void onPostExecute(Void v) {
ReposFragment.repoLoadDone.trigger();
}
}
public static class LoadApps extends RootTask<Void, Void, LoadApps.Result> {
private PackageManager pm;
public LoadApps(PackageManager packageManager) {
pm = packageManager;
}
@Override
protected Result doInBackground(Void... voids) {
List<ApplicationInfo> listApps = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (Iterator<ApplicationInfo> i = listApps.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next();
if (MagiskHideFragment.BLACKLIST.contains(info.packageName) || !info.enabled)
i.remove();
}
Collections.sort(listApps, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
.compareTo(b.loadLabel(pm).toString().toLowerCase()));
List<String> hideList = Shell.su(Async.MAGISK_HIDE_PATH + "list");
return new Result(listApps, hideList);
}
@Override
protected void onPostExecute(Result result) {
MagiskHideFragment.packageLoadDone.trigger(result);
}
public static class Result {
public final List<ApplicationInfo> listApps;
public final List<String> hideList;
Result(List<ApplicationInfo> listApps, List<String> hideList) {
this.listApps = listApps;
this.hideList = hideList;
}
}
}
public static class FlashZIP extends RootTask<Void, String, Integer> {
protected Uri mUri;
protected File mCachedFile;
private String mFilename;
protected ProgressDialog progress;
private Context mContext;
public FlashZIP(Context context, Uri uri, String filename) {
mContext = context;
mUri = uri;
mFilename = filename;
}
public FlashZIP(Context context, Uri uri) {
mContext = context;
mUri = uri;
// Try to get the filename ourselves
Cursor c = mContext.getContentResolver().query(uri, null, null, null, null);
if (c != null) {
int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
c.moveToFirst();
if (nameIndex != -1) {
mFilename = c.getString(nameIndex);
}
c.close();
}
if (mFilename == null) {
int idx = uri.getPath().lastIndexOf('/');
mFilename = uri.getPath().substring(idx + 1);
}
}
protected void preProcessing() throws Throwable {}
protected void copyToCache() throws Throwable {
publishProgress(mContext.getString(R.string.copying_msg));
try {
InputStream in = mContext.getContentResolver().openInputStream(mUri);
mCachedFile = new File(mContext.getCacheDir().getAbsolutePath() + "/install.zip");
if (mCachedFile.exists() && !mCachedFile.delete()) {
throw new IOException();
}
OutputStream outputStream = new FileOutputStream(mCachedFile);
byte buffer[] = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
outputStream.close();
Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath());
in.close();
} catch (FileNotFoundException e) {
Log.e(Logger.TAG, "FlashZip: Invalid Uri");
throw e;
} catch (IOException e) {
Log.e(Logger.TAG, "FlashZip: Error in creating file");
throw e;
}
}
protected boolean unzipAndCheck() {
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android");
return Utils.readFile(mCachedFile.getParent() + "/META-INF/com/google/android/updater-script")
.get(0).contains("#MAGISK");
}
@Override
protected void onPreExecute() {
progress = new ProgressDialog(mContext);
progress.setTitle(R.string.zip_install_progress_title);
progress.show();
}
@Override
protected void onProgressUpdate(String... values) {
progress.setMessage(values[0]);
}
@Override
protected Integer doInBackground(Void... voids) {
Logger.dev("FlashZip Running... " + mFilename);
List<String> ret;
try {
preProcessing();
copyToCache();
} catch (Throwable e) {
e.printStackTrace();
return -1;
}
if (!unzipAndCheck()) return 0;
publishProgress(mContext.getString(R.string.zip_install_progress_msg, mFilename));
ret = Shell.su(
"BOOTMODE=true sh " + mCachedFile.getParent() +
"/META-INF/com/google/android/update-binary dummy 1 " + mCachedFile.getPath(),
"if [ $? -eq 0 ]; then echo true; else echo false; fi"
);
Logger.dev("FlashZip: Console log:");
for (String line : ret) {
Logger.dev(line);
}
Shell.su(
"rm -rf " + mCachedFile.getParent() + "/*",
"rm -rf " + TMP_FOLDER_PATH
);
if (Boolean.parseBoolean(ret.get(ret.size() - 1))) {
return 1;
}
return -1;
}
// -1 = error, manual install; 0 = invalid zip; 1 = success
@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
progress.dismiss();
switch (result) {
case -1:
Toast.makeText(mContext, mContext.getString(R.string.install_error), Toast.LENGTH_LONG).show();
Toast.makeText(mContext, mContext.getString(R.string.manual_install_1, mUri.getPath()), Toast.LENGTH_LONG).show();
Toast.makeText(mContext, mContext.getString(R.string.manual_install_2), Toast.LENGTH_LONG).show();
break;
case 0:
Toast.makeText(mContext, mContext.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show();
break;
case 1:
onSuccess();
break;
}
}
protected void onSuccess() {
StatusFragment.updateCheckDone.trigger();
new LoadModules().exec();
Utils.getAlertDialogBuilder(mContext)
.setTitle(R.string.reboot_title)
.setMessage(R.string.reboot_msg)
.setPositiveButton(R.string.reboot, (dialogInterface1, i) -> Shell.sh("su -c reboot"))
.setNegativeButton(R.string.no_thanks, null)
.show();
}
}
public static class MagiskHide extends RootTask<Object, Void, Void> {
@Override
protected Void doInBackground(Object... params) {
boolean add = (boolean) params[0];
String packageName = (String) params[1];
Shell.su(MAGISK_HIDE_PATH + (add ? "add " : "rm ") + packageName);
return null;
}
public void add(CharSequence packageName) {
exec(true, packageName);
}
public void rm(CharSequence packageName) {
exec(false, packageName);
}
}
public static class GetBootBlocks extends RootTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
if (Shell.rootAccess()) {
InstallFragment.blockList = Shell.su("ls /dev/block | grep mmc");
if (InstallFragment.bootBlock == null) {
InstallFragment.bootBlock = Utils.detectBootImage();
}
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
InstallFragment.blockDetectionDone.trigger();
}
}
}

View File

@@ -1,58 +0,0 @@
package com.topjohnwu.magisk.utils;
import java.util.HashMap;
import java.util.HashSet;
public class CallbackHandler {
private static HashMap<Event, HashSet<EventListener>> listeners = new HashMap<>();
public static void register(Event event, EventListener listener) {
HashSet<EventListener> list = listeners.get(event);
if (list == null) {
list = new HashSet<>();
listeners.put(event, list);
}
list.add(listener);
}
public static void unRegister(Event event, EventListener listener) {
HashSet<EventListener> list = listeners.get(event);
if (list != null) {
list.remove(listener);
}
}
private static void triggerCallback(Event event) {
HashSet<EventListener> list = listeners.get(event);
if (list != null) {
for (EventListener listener : list) {
listener.onTrigger(event);
}
}
}
public static class Event {
public boolean isTriggered = false;
private Object result;
public void trigger() {
trigger(null);
}
public void trigger(Object result) {
this.result = result;
isTriggered = true;
triggerCallback(this);
}
public Object getResult() {
return result;
}
}
public interface EventListener {
void onTrigger(Event event);
}
}

View File

@@ -1,38 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.util.Log;
public class Logger {
public static final String TAG = "Magisk";
public static final String DEV_TAG = "Magisk: DEV";
public static final String DEBUG_TAG = "Magisk: DEBUG";
public static boolean logShell, devLog;
public static void debug(String msg) {
Log.d(DEBUG_TAG, msg);
}
public static void dev(String msg, Object... args) {
if (devLog) {
if (args.length == 1 && args[0] instanceof Throwable) {
Log.d(DEV_TAG, msg, (Throwable) args[0]);
} else {
Log.d(DEV_TAG, String.format(msg, args));
}
}
}
public static void dev(String msg) {
if (devLog) {
Log.d(DEV_TAG, msg);
}
}
public static void shell(boolean root, String msg) {
if (logShell) {
Log.d(root ? "SU" : "SH", msg);
}
}
}

View File

@@ -1,203 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.module.BaseModule;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.module.Repo;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class ModuleHelper {
private static final String MAGISK_PATH = "/magisk";
public static final String FILE_KEY = "RepoMap";
private static final String REPO_KEY = "repomap";
public static final String VERSION_KEY = "version";
public static final String ETAG_KEY = "ETag";
private static final int DATABASE_VER = 1;
private static ValueSortedMap<String, Repo> repoMap = new ValueSortedMap<>();
private static ValueSortedMap<String, Module> moduleMap = new ValueSortedMap<>();
public static void createModuleMap() {
Logger.dev("ModuleHelper: Loading modules");
moduleMap.clear();
for (String path : Utils.getModList(MAGISK_PATH)) {
Logger.dev("ModuleHelper: Adding modules from " + path);
Module module;
try {
module = new Module(path);
moduleMap.put(module.getId(), module);
} catch (BaseModule.CacheModException ignored) {}
}
Logger.dev("ModuleHelper: Module load done");
}
public static void createRepoMap(Context context) {
Logger.dev("ModuleHelper: Loading repos");
SharedPreferences prefs = context.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE);
repoMap.clear();
Gson gson = new Gson();
String jsonString;
int cachedVersion = prefs.getInt(VERSION_KEY, 0);
if (cachedVersion != DATABASE_VER) {
// Ignore incompatible cached database
jsonString = null;
} else {
jsonString = prefs.getString(REPO_KEY, null);
}
ValueSortedMap<String, Repo> cached = null;
if (jsonString != null) {
cached = gson.fromJson(jsonString, new TypeToken<ValueSortedMap<String, Repo>>(){}.getType());
}
if (cached == null) {
cached = new ValueSortedMap<>();
}
// Get cached ETag to add in the request header
String etag = prefs.getString(ETAG_KEY, "");
HashMap<String, String> header = new HashMap<>();
header.put("If-None-Match", etag);
// Making a request to main URL for repo info
jsonString = WebService.request(
context.getString(R.string.url_main), WebService.GET, null, header, false);
if (!jsonString.isEmpty()) {
try {
JSONArray jsonArray = new JSONArray(jsonString);
// If it gets to this point, the response is valid, update ETag
etag = WebService.getLastResponseHeader().get(ETAG_KEY).get(0);
// Maybe bug in Android build tools, sometimes the ETag has crap in it...
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
// Update repo info
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonobject = jsonArray.getJSONObject(i);
String id = jsonobject.getString("description");
String name = jsonobject.getString("name");
String lastUpdate = jsonobject.getString("pushed_at");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
Date updatedDate;
try {
updatedDate = format.parse(lastUpdate);
} catch (ParseException e) {
continue;
}
Repo repo = cached.get(id);
try {
if (repo == null) {
Logger.dev("ModuleHelper: Create new repo " + id);
repo = new Repo(context, name, updatedDate);
} else {
Logger.dev("ModuleHelper: Update cached repo " + id);
repo.update(updatedDate);
}
if (repo.getId() != null) {
repoMap.put(id, repo);
}
} catch (BaseModule.CacheModException ignored) {}
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
// Use cached if no internet or no updates
Logger.dev("ModuleHelper: No updates, use cached");
repoMap.putAll(cached);
}
prefs.edit()
.putInt(VERSION_KEY, DATABASE_VER)
.putString(REPO_KEY, gson.toJson(repoMap))
.putString(ETAG_KEY, etag)
.apply();
Logger.dev("ModuleHelper: Repo load done");
}
public static void getModuleList(List<Module> moduleList) {
moduleList.clear();
moduleList.addAll(moduleMap.values());
}
public static void getRepoLists(List<Repo> update, List<Repo> installed, List<Repo> others) {
update.clear();
installed.clear();
others.clear();
for (Repo repo : repoMap.values()) {
Module module = moduleMap.get(repo.getId());
if (module != null) {
if (repo.getVersionCode() > module.getVersionCode()) {
update.add(repo);
} else {
installed.add(repo);
}
} else {
others.add(repo);
}
}
}
private static class ValueSortedMap<K, V extends Comparable > extends HashMap<K, V> {
private List<V> sorted = new ArrayList<>();
@NonNull
@Override
public Collection<V> values() {
if (sorted.isEmpty()) {
sorted.addAll(super.values());
Collections.sort(sorted);
}
return sorted;
}
@Override
public V put(K key, V value) {
sorted.clear();
return super.put(key, value);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
sorted.clear();
super.putAll(m);
}
@Override
public V remove(Object key) {
sorted.clear();
return super.remove(key);
}
}
}

View File

@@ -1,83 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Base64;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.safetynet.SafetyNet;
import org.json.JSONException;
import org.json.JSONObject;
import java.security.SecureRandom;
public abstract class SafetyNetHelper
implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks {
private GoogleApiClient mGoogleApiClient;
public SafetyNetHelper(Context context) {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(SafetyNet.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult result) {
Logger.dev("SN: Google API fail");
handleResults(-2);
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Logger.dev("SN: Google API Connected");
safetyNetCheck();
}
@Override
public void onConnectionSuspended(int i) {
Logger.dev("SN: Google API Suspended");
handleResults(-3);
}
public void requestTest() {
// Connect Google Service
mGoogleApiClient.connect();
}
private void safetyNetCheck() {
// Create nonce
byte[] nonce = new byte[24];
new SecureRandom().nextBytes(nonce);
Logger.dev("SN: Check with nonce: " + Base64.encodeToString(nonce, Base64.DEFAULT));
// Call SafetyNet
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
.setResultCallback(result -> {
Status status = result.getStatus();
if (status.isSuccess()) {
String json = new String(Base64.decode(result.getJwsResult().split("\\.")[1], Base64.DEFAULT));
Logger.dev("SN: Response: " + json);
try {
JSONObject decoded = new JSONObject(json);
handleResults(decoded.getBoolean("ctsProfileMatch") ? 1 : 0);
} catch (JSONException ignored) {}
} else {
Logger.dev("SN: No response");
handleResults(-1);
}
// Disconnect
mGoogleApiClient.disconnect();
});
}
public abstract void handleResults(int i);
}

View File

@@ -1,202 +0,0 @@
package com.topjohnwu.magisk.utils;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Modified by topjohnwu, based on Chainfire's libsuperuser
*/
public class Shell {
// -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted
public static int rootStatus;
private static Process rootShell;
private static DataOutputStream rootSTDIN;
private static StreamGobbler rootSTDOUT;
private static List<String> rootOutList = new ArrayList<>();
static {
init();
}
private static void init() {
try {
rootShell = Runtime.getRuntime().exec("su");
rootStatus = 1;
} catch (IOException err) {
// No root
rootStatus = 0;
return;
}
rootSTDIN = new DataOutputStream(rootShell.getOutputStream());
rootSTDOUT = new StreamGobbler(rootShell.getInputStream(), rootOutList, true);
rootSTDOUT.start();
// Setup umask and PATH
su("umask 022");
su("PATH=/data/busybox:$PATH");
List<String> ret = su("echo -BOC-", "id");
if (ret == null) {
// Something wrong with root, not allowed?
rootStatus = -1;
return;
}
for (String line : ret) {
if (line.contains("uid=")) {
// id command is working, let's see if we are actually root
rootStatus = line.contains("uid=0") ? rootStatus : -1;
return;
} else if (!line.contains("-BOC-")) {
rootStatus = -1;
return;
}
}
}
public static boolean rootAccess() {
return rootStatus > 0;
}
public static List<String> sh(String... commands) {
List<String> res = Collections.synchronizedList(new ArrayList<String>());
try {
Process process = Runtime.getRuntime().exec("sh");
DataOutputStream STDIN = new DataOutputStream(process.getOutputStream());
StreamGobbler STDOUT = new StreamGobbler(process.getInputStream(), res);
STDOUT.start();
try {
for (String write : commands) {
STDIN.write((write + "\n").getBytes("UTF-8"));
STDIN.flush();
Logger.shell(false, write);
}
STDIN.write("exit\n".getBytes("UTF-8"));
STDIN.flush();
} catch (IOException e) {
if (!e.getMessage().contains("EPIPE")) {
throw e;
}
}
process.waitFor();
try {
STDIN.close();
} catch (IOException e) {
// might be closed already
}
STDOUT.join();
process.destroy();
} catch (IOException | InterruptedException e) {
// shell probably not found
res = null;
}
return res;
}
// Run with the same shell by default
public static List<String> su(String... commands) {
return su(false, commands);
}
public static List<String> su(boolean newShell, String... commands) {
List<String> res;
Process process;
DataOutputStream STDIN;
StreamGobbler STDOUT;
if (!rootAccess()) {
return null;
}
if (newShell) {
res = Collections.synchronizedList(new ArrayList<String>());
try {
process = Runtime.getRuntime().exec("su");
STDIN = new DataOutputStream(process.getOutputStream());
STDOUT = new StreamGobbler(process.getInputStream(), res);
} catch (IOException err) {
return null;
}
STDOUT.start();
} else {
process = rootShell;
STDIN = rootSTDIN;
STDOUT = rootSTDOUT;
res = rootOutList;
res.clear();
}
try {
for (String write : commands) {
STDIN.write((write + "\n").getBytes("UTF-8"));
STDIN.flush();
Logger.shell(true, write);
}
if (newShell) {
STDIN.write("exit\n".getBytes("UTF-8"));
STDIN.flush();
process.waitFor();
try {
STDIN.close();
} catch (IOException ignore) {
// might be closed already
}
STDOUT.join();
process.destroy();
} else {
STDIN.write(("echo\n").getBytes("UTF-8"));
STDIN.flush();
STDIN.write(("echo \'-root-done-\'\n").getBytes("UTF-8"));
STDIN.flush();
while (true) {
try {
// Process terminated, it means the interactive shell has some issues
process.exitValue();
return null;
} catch (IllegalThreadStateException e) {
// Process still running, gobble output until done
int end = res.size() - 1;
if (end > 0) {
if (res.get(end).equals("-root-done-")) {
res.remove(end);
if (res.get(end -1).isEmpty()) {
res.remove(end -1);
}
break;
}
}
try { STDOUT.join(100); } catch (InterruptedException err) { return null; }
}
}
}
} catch (IOException e) {
if (!e.getMessage().contains("EPIPE")) {
Logger.dev("Shell: Root shell error...");
return null;
}
} catch(InterruptedException e) {
Logger.dev("Shell: Root shell error...");
return null;
}
return new ArrayList<>(res);
}
}

View File

@@ -1,62 +0,0 @@
package com.topjohnwu.magisk.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
/**
* Modified by topjohnwu, based on Chainfire's libsuperuser
*/
public class StreamGobbler extends Thread {
private BufferedReader reader = null;
private List<String> writer = null;
private boolean isRoot = false;
/**
* <p>StreamGobbler constructor</p>
*
* <p>We use this class because shell STDOUT and STDERR should be read as quickly as
* possible to prevent a deadlock from occurring, or Process.waitFor() never
* returning (as the buffer is full, pausing the native process)</p>
*
* @param inputStream InputStream to read from
* @param outputList {@literal List<String>} to write to, or null
*/
public StreamGobbler(InputStream inputStream, List<String> outputList) {
reader = new BufferedReader(new InputStreamReader(inputStream));
writer = outputList;
}
public StreamGobbler(InputStream inputStream, List<String> outputList, boolean root) {
reader = new BufferedReader(new InputStreamReader(inputStream));
writer = outputList;
isRoot = root;
}
@Override
public void run() {
// keep reading the InputStream until it ends (or an error occurs)
try {
String line;
while ((line = reader.readLine()) != null) {
writer.add(line);
if (!line.equals("-root-done-") && !line.isEmpty()) {
Logger.shell(isRoot, "OUT: " + line);
}
}
} catch (IOException e) {
// reader probably closed, expected exit condition
}
// make sure our stream is closed and resources will be freed
try {
reader.close();
} catch (IOException e) {
// read already closed
}
}
}

View File

@@ -1,152 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.Manifest;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.text.TextUtils;
import android.widget.Toast;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.List;
public class Utils {
public static boolean isDownloading = false;
public static boolean isDarkTheme;
public static boolean itemExist(String path) {
return itemExist(true, path);
}
public static boolean itemExist(boolean root, String path) {
String command = "if [ -e " + path + " ]; then echo true; else echo false; fi";
if (Shell.rootAccess() && root) {
return Boolean.parseBoolean(Shell.su(command).get(0));
} else {
return new File(path).exists();
}
}
public static boolean commandExists(String s) {
List<String> ret;
String command = "if [ -z $(which " + s + ") ]; then echo false; else echo true; fi";
ret = Shell.sh(command);
return Boolean.parseBoolean(ret.get(0));
}
public static boolean createFile(String path) {
String folder = path.substring(0, path.lastIndexOf('/'));
String command = "mkdir -p " + folder + " 2>/dev/null; touch " + path + " 2>/dev/null; if [ -f \"" + path + "\" ]; then echo true; else echo false; fi";
return Shell.rootAccess() && Boolean.parseBoolean(Shell.su(command).get(0));
}
public static boolean removeItem(String path) {
String command = "rm -rf " + path + " 2>/dev/null; if [ -e " + path + " ]; then echo false; else echo true; fi";
return Shell.rootAccess() && Boolean.parseBoolean(Shell.su(command).get(0));
}
public static List<String> getModList(String path) {
List<String> ret;
String command = "find " + path + " -type d -maxdepth 1 ! -name \"*.core\" ! -name \"*lost+found\" ! -name \"*magisk\"";
ret = Shell.su(command);
return ret;
}
public static List<String> readFile(String path) {
List<String> ret;
String command = "cat " + path;
if (Shell.rootAccess()) {
ret = Shell.su(command);
} else {
ret = Shell.sh(command);
}
return ret;
}
public static void dlAndReceive(Context context, DownloadReceiver receiver, String link, String filename) {
if (isDownloading) {
return;
}
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(context, R.string.permissionNotGranted, Toast.LENGTH_LONG).show();
return;
}
File file = new File(Environment.getExternalStorageDirectory() + "/MagiskManager/" + filename);
if ((!file.getParentFile().exists() && !file.getParentFile().mkdirs()) || (file.exists() && !file.delete())) {
Toast.makeText(context, R.string.permissionNotGranted, Toast.LENGTH_LONG).show();
return;
}
Toast.makeText(context, context.getString(R.string.downloading_toast, filename), Toast.LENGTH_LONG).show();
isDownloading = true;
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(link));
request.setDestinationUri(Uri.fromFile(file));
receiver.setDownloadID(downloadManager.enqueue(request));
receiver.setFilename(filename);
context.registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
public static String getLegalFilename(CharSequence filename) {
return filename.toString().replace(" ", "_").replace("'", "").replace("\"", "")
.replace("$", "").replace("`", "").replace("(", "").replace(")", "")
.replace("#", "").replace("@", "").replace("*", "");
}
public static String detectBootImage() {
String[] commands = {
"for PARTITION in kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do",
"BOOTIMAGE=`readlink /dev/block/by-name/$PARTITION || readlink /dev/block/platform/*/by-name/$PARTITION || readlink /dev/block/platform/*/*/by-name/$PARTITION`",
"if [ ! -z \"$BOOTIMAGE\" ]; then break; fi",
"done",
"echo \"${BOOTIMAGE##*/}\""
};
List<String> ret = Shell.su(commands);
if (!ret.isEmpty()) {
return ret.get(0);
}
return null;
}
public static AlertDialog.Builder getAlertDialogBuilder(Context context) {
if (isDarkTheme) {
return new AlertDialog.Builder(context, R.style.AlertDialog_dh);
} else {
return new AlertDialog.Builder(context);
}
}
public static boolean lowercaseContains(CharSequence string, CharSequence nonNullLowercaseSearch) {
return !TextUtils.isEmpty(string) && string.toString().toLowerCase().contains(nonNullLowercaseSearch);
}
public static class ByteArrayInOutStream extends ByteArrayOutputStream {
public ByteArrayInputStream getInputStream() {
ByteArrayInputStream in = new ByteArrayInputStream(buf, 0, count);
count = 0;
buf = new byte[32];
return in;
}
public void setBuffer(byte[] buffer) {
buf = buffer;
count = buffer.length;
}
}
}

View File

@@ -1,124 +0,0 @@
package com.topjohnwu.magisk.utils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
public class WebService {
public final static int GET = 1;
public final static int POST = 2;
private static Map<String, List<String>> responseHeader;
/**
* Making web service call
*
* @url - url to make request
* @requestmethod - http request method
*/
public static String request(String url, int method) {
return request(url, method, null, null, false);
}
public static String request(String url, int method, boolean newline) {
return request(url, method, null, null, newline);
}
/**
* Making service call
*
* @url - url to make request
* @requestmethod - http request method
* @params - http request params
* @header - http request header
* @newline - true to append a newline each line
*/
public static String request(String urlAddress, int method,
Map<String, String> params, Map<String, String> header,
boolean newline) {
Logger.dev("WebService: Service call " + urlAddress);
URL url;
StringBuilder response = new StringBuilder();
try {
url = new URL(urlAddress);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(15000);
conn.setConnectTimeout(15000);
conn.setDoInput(true);
if (method == POST) {
conn.setRequestMethod("POST");
} else if (method == GET) {
conn.setRequestMethod("GET");
}
if (header != null) {
for (Map.Entry<String, String> entry : header.entrySet()) {
conn.setRequestProperty(entry.getKey(), entry.getValue());
}
}
if (params != null) {
OutputStream os = conn.getOutputStream();
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));
StringBuilder result = new StringBuilder();
boolean first = true;
for (Map.Entry<String, String> entry : params.entrySet()) {
if (first)
first = false;
else
result.append("&");
result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
result.append("=");
result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
}
writer.write(result.toString());
writer.flush();
writer.close();
os.close();
}
int responseCode = conn.getResponseCode();
if (responseCode == HttpsURLConnection.HTTP_OK) {
String line;
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
while ((line = br.readLine()) != null) {
if (newline) {
response.append(line).append("\n");
} else {
response.append(line);
}
}
responseHeader = conn.getHeaderFields();
} else {
responseHeader = null;
}
} catch (Exception e) {
e.printStackTrace();
}
return response.toString();
}
public static Map<String, List<String>> getLastResponseHeader() {
return responseHeader;
}
}

View File

@@ -1,32 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.app.AlertDialog;
import android.content.Context;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class WebWindow {
public WebWindow(String title, String url, Context context) {
AlertDialog.Builder alert = Utils.getAlertDialogBuilder(context);
alert.setTitle(title);
Logger.dev("WebView: URL = " + url);
WebView wv = new WebView(context);
wv.loadUrl(url);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(url);
return true;
}
});
alert.setView(wv);
alert.setNegativeButton("Close", (dialog, id) -> dialog.dismiss());
alert.show();
}
}

View File

@@ -1,747 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.content.Context;
import android.util.Pair;
import com.topjohnwu.magisk.utils.Utils.ByteArrayInOutStream;
import org.spongycastle.asn1.ASN1InputStream;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.DEROutputStream;
import org.spongycastle.asn1.cms.CMSObjectIdentifiers;
import org.spongycastle.cert.jcajce.JcaCertStore;
import org.spongycastle.cms.CMSException;
import org.spongycastle.cms.CMSProcessableByteArray;
import org.spongycastle.cms.CMSSignedData;
import org.spongycastle.cms.CMSSignedDataGenerator;
import org.spongycastle.cms.CMSTypedData;
import org.spongycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import org.spongycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.spongycastle.util.encoders.Base64;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.security.DigestOutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public class ZipUtils {
private static final String CERT_SF_NAME = "META-INF/CERT.SF";
private static final String CERT_SIG_NAME = "META-INF/CERT.%s";
private static final String OTACERT_NAME = "META-INF/com/android/otacert";
private static final String PUBLIC_KEY_NAME = "public.certificate.x509.pem";
private static final String PRIVATE_KEY_NAME = "private.key.pk8";
private static Provider sBouncyCastleProvider;
// bitmasks for which hash algorithms we need the manifest to include.
private static final int USE_SHA1 = 1;
private static final int USE_SHA256 = 2;
static {
System.loadLibrary("zipadjust");
sBouncyCastleProvider = new BouncyCastleProvider();
Security.insertProviderAt(sBouncyCastleProvider, 1);
}
public native static byte[] zipAdjust(byte[] bytes, int size);
// Wrapper function for the JNI function
public static void adjustZip(ByteArrayInOutStream buffer) {
buffer.setBuffer(zipAdjust(buffer.toByteArray(), buffer.size()));
}
public static void removeTopFolder(InputStream in, OutputStream out) {
try {
JarInputStream source = new JarInputStream(in);
JarOutputStream dest = new JarOutputStream(out);
JarEntry entry;
String path;
int size;
byte buffer[] = new byte[4096];
while ((entry = source.getNextJarEntry()) != null) {
// Remove the top directory from the path
path = entry.getName().substring(entry.getName().indexOf("/") + 1);
// If it's the top folder, ignore it
if (path.isEmpty())
continue;
// Don't include placeholder
if (path.contains("system/placeholder"))
continue;
dest.putNextEntry(new JarEntry(path));
while((size = source.read(buffer, 0, 2048)) != -1)
dest.write(buffer, 0, size);
}
source.close();
dest.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
Logger.dev("ZipUtils: removeTopFolder IO error!");
}
}
public static void unzip(File file, File folder) {
unzip(file, folder, "");
}
public static void unzip(File file, File folder, String path) {
try {
int count;
FileOutputStream out;
File dest;
InputStream is;
JarEntry entry;
byte data[] = new byte[4096];
JarFile zipfile = new JarFile(file);
Enumeration e = zipfile.entries();
while(e.hasMoreElements()) {
entry = (JarEntry) e.nextElement();
if (!entry.getName().contains(path)
|| entry.getName().charAt(entry.getName().length() - 1) == '/') {
// Ignore directories, only create files
continue;
}
Logger.dev("ZipUtils: Extracting: " + entry);
is = zipfile.getInputStream(entry);
dest = new File(folder, entry.getName());
if (dest.getParentFile().mkdirs()) {
dest.createNewFile();
}
out = new FileOutputStream(dest);
while ((count = is.read(data, 0, 4096)) != -1) {
out.write(data, 0, count);
}
out.flush();
out.close();
is.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
public static void signZip(Context context, InputStream inputStream,
OutputStream outputStream, boolean signWholeFile) {
JarMap inputJar;
int hashes = 0;
try {
X509Certificate publicKey = readPublicKey(context.getAssets().open(PUBLIC_KEY_NAME));
hashes |= getDigestAlgorithm(publicKey);
// Set the ZIP file timestamp to the starting valid time
// of the 0th certificate plus one hour (to match what
// we've historically done).
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
PrivateKey privateKey = readPrivateKey(context.getAssets().open(PRIVATE_KEY_NAME));
inputJar = new JarMap(new JarInputStream(inputStream));
if (signWholeFile) {
if (!"RSA".equalsIgnoreCase(privateKey.getAlgorithm())) {
System.err.println("Cannot sign OTA packages with non-RSA keys");
System.exit(1);
}
signWholeFile(inputJar, context.getAssets().open(PUBLIC_KEY_NAME),
publicKey, privateKey, outputStream);
} else {
JarOutputStream outputJar = new JarOutputStream(outputStream);
// For signing .apks, use the maximum compression to make
// them as small as possible (since they live forever on
// the system partition). For OTA packages, use the
// default compression level, which is much much faster
// and produces output that is only a tiny bit larger
// (~0.1% on full OTA packages I tested).
outputJar.setLevel(9);
Manifest manifest = addDigestsToManifest(inputJar, hashes);
copyFiles(manifest, inputJar, outputJar, timestamp);
signFile(manifest, inputJar, publicKey, privateKey, outputJar);
outputJar.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static class JarMap extends TreeMap<String, Pair<JarEntry, ByteArrayOutputStream> > {
private Manifest manifest;
public JarMap(JarInputStream in) throws IOException {
super();
manifest = in.getManifest();
byte[] buffer = new byte[4096];
int num;
JarEntry entry;
while ((entry = in.getNextJarEntry()) != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
while ((num = in.read(buffer)) > 0) {
stream.write(buffer, 0, num);
}
put(entry.getName(), entry, stream);
}
in.close();
}
public JarEntry getJarEntry(String name) {
return get(name).first;
}
public ByteArrayOutputStream getStream(String name) {
return get(name).second;
}
public void put(String name, JarEntry entry, ByteArrayOutputStream stream) {
put(name, new Pair<>(entry, stream));
}
public Manifest getManifest() {
return manifest;
}
}
/**
* Return one of USE_SHA1 or USE_SHA256 according to the signature
* algorithm specified in the cert.
*/
private static int getDigestAlgorithm(X509Certificate cert) {
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
if ("SHA1WITHRSA".equals(sigAlg) ||
"MD5WITHRSA".equals(sigAlg)) { // see "HISTORICAL NOTE" above.
return USE_SHA1;
} else if (sigAlg.startsWith("SHA256WITH")) {
return USE_SHA256;
} else {
throw new IllegalArgumentException("unsupported signature algorithm \"" + sigAlg +
"\" in cert [" + cert.getSubjectDN());
}
}
/** Returns the expected signature algorithm for this key type. */
private static String getSignatureAlgorithm(X509Certificate cert) {
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
String keyType = cert.getPublicKey().getAlgorithm().toUpperCase(Locale.US);
if ("RSA".equalsIgnoreCase(keyType)) {
if (getDigestAlgorithm(cert) == USE_SHA256) {
return "SHA256withRSA";
} else {
return "SHA1withRSA";
}
} else if ("DSA".equalsIgnoreCase(keyType)) {
return "SHA256withDSA";
} else if ("EC".equalsIgnoreCase(keyType)) {
return "SHA256withECDSA";
} else {
throw new IllegalArgumentException("unsupported key type: " + keyType);
}
}
// Files matching this pattern are not copied to the output.
private static Pattern stripPattern =
Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" +
Pattern.quote(JarFile.MANIFEST_NAME) + ")$");
private static X509Certificate readPublicKey(InputStream input)
throws IOException, GeneralSecurityException {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(input);
} finally {
input.close();
}
}
/**
* Decrypt an encrypted PKCS 8 format private key.
*
* Based on ghstark's post on Aug 6, 2006 at
* http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949
*
* @param encryptedPrivateKey The raw data of the private key
* @param keyFile The file containing the private key
*/
private static KeySpec decryptPrivateKey(byte[] encryptedPrivateKey, File keyFile)
throws GeneralSecurityException {
EncryptedPrivateKeyInfo epkInfo;
try {
epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey);
} catch (IOException ex) {
// Probably not an encrypted key.
return null;
}
// We no longer have console, so need to use another way to input password
// This function is left here if needed in the future, so no use for now
char[] password = new char[0];
SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName());
Key key = skFactory.generateSecret(new PBEKeySpec(password));
Cipher cipher = Cipher.getInstance(epkInfo.getAlgName());
cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters());
try {
return epkInfo.getKeySpec(cipher);
} catch (InvalidKeySpecException ex) {
System.err.println("signapk: Password for " + keyFile + " may be bad.");
throw ex;
}
}
/** Read a PKCS 8 format private key. */
private static PrivateKey readPrivateKey(InputStream input)
throws IOException, GeneralSecurityException {
try {
byte[] buffer = new byte[4096];
int size = input.read(buffer);
byte[] bytes = Arrays.copyOf(buffer, size);
KeySpec spec = new PKCS8EncodedKeySpec(bytes);
PrivateKey key;
key = decodeAsKeyType(spec, "RSA");
if (key != null) {
return key;
}
key = decodeAsKeyType(spec, "DSA");
if (key != null) {
return key;
}
key = decodeAsKeyType(spec, "EC");
if (key != null) {
return key;
}
throw new NoSuchAlgorithmException("Must be an RSA, DSA, or EC key");
} finally {
input.close();
}
}
private static PrivateKey decodeAsKeyType(KeySpec spec, String keyType)
throws GeneralSecurityException {
try {
return KeyFactory.getInstance(keyType).generatePrivate(spec);
} catch (InvalidKeySpecException e) {
return null;
}
}
/**
* Add the hash(es) of every file to the manifest, creating it if
* necessary.
*/
private static Manifest addDigestsToManifest(JarMap jar, int hashes)
throws IOException, GeneralSecurityException {
Manifest input = jar.getManifest();
Manifest output = new Manifest();
Attributes main = output.getMainAttributes();
if (input != null) {
main.putAll(input.getMainAttributes());
} else {
main.putValue("Manifest-Version", "1.0");
main.putValue("Created-By", "1.0 (Android SignApk)");
}
MessageDigest md_sha1 = null;
MessageDigest md_sha256 = null;
if ((hashes & USE_SHA1) != 0) {
md_sha1 = MessageDigest.getInstance("SHA1");
}
if ((hashes & USE_SHA256) != 0) {
md_sha256 = MessageDigest.getInstance("SHA256");
}
// We sort the input entries by name, and add them to the
// output manifest in sorted order. We expect that the output
// map will be deterministic.
/* JarMap is a TreeMap, so it's already sorted */
for (String name : jar.keySet()) {
JarEntry entry = jar.getJarEntry(name);
if (!entry.isDirectory() &&
(stripPattern == null || !stripPattern.matcher(name).matches())) {
byte[] buffer = jar.getStream(name).toByteArray();
if (md_sha1 != null) md_sha1.update(buffer, 0, buffer.length);
if (md_sha256 != null) md_sha256.update(buffer, 0, buffer.length);
Attributes attr = null;
if (input != null) attr = input.getAttributes(name);
attr = attr != null ? new Attributes(attr) : new Attributes();
if (md_sha1 != null) {
attr.putValue("SHA1-Digest",
new String(Base64.encode(md_sha1.digest()), "ASCII"));
}
if (md_sha256 != null) {
attr.putValue("SHA-256-Digest",
new String(Base64.encode(md_sha256.digest()), "ASCII"));
}
output.getEntries().put(name, attr);
}
}
return output;
}
/**
* Add a copy of the public key to the archive; this should
* exactly match one of the files in
* /system/etc/security/otacerts.zip on the device. (The same
* cert can be extracted from the CERT.RSA file but this is much
* easier to get at.)
*/
private static void addOtacert(JarOutputStream outputJar,
InputStream input,
long timestamp,
Manifest manifest,
int hash)
throws IOException, GeneralSecurityException {
MessageDigest md = MessageDigest.getInstance(hash == USE_SHA1 ? "SHA1" : "SHA256");
JarEntry je = new JarEntry(OTACERT_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
byte[] b = new byte[4096];
int read;
while ((read = input.read(b)) != -1) {
outputJar.write(b, 0, read);
md.update(b, 0, read);
}
input.close();
Attributes attr = new Attributes();
attr.putValue(hash == USE_SHA1 ? "SHA1-Digest" : "SHA-256-Digest",
new String(Base64.encode(md.digest()), "ASCII"));
manifest.getEntries().put(OTACERT_NAME, attr);
}
/** Write to another stream and track how many bytes have been
* written.
*/
private static class CountOutputStream extends FilterOutputStream {
private int mCount;
public CountOutputStream(OutputStream out) {
super(out);
mCount = 0;
}
@Override
public void write(int b) throws IOException {
super.write(b);
mCount++;
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
mCount += len;
}
public int size() {
return mCount;
}
}
/** Write a .SF file with a digest of the specified manifest. */
private static void writeSignatureFile(Manifest manifest, OutputStream out,
int hash)
throws IOException, GeneralSecurityException {
Manifest sf = new Manifest();
Attributes main = sf.getMainAttributes();
main.putValue("Signature-Version", "1.0");
main.putValue("Created-By", "1.0 (Android SignApk)");
MessageDigest md = MessageDigest.getInstance(
hash == USE_SHA256 ? "SHA256" : "SHA1");
PrintStream print = new PrintStream(
new DigestOutputStream(new ByteArrayOutputStream(), md),
true, "UTF-8");
// Digest of the entire manifest
manifest.write(print);
print.flush();
main.putValue(hash == USE_SHA256 ? "SHA-256-Digest-Manifest" : "SHA1-Digest-Manifest",
new String(Base64.encode(md.digest()), "ASCII"));
Map<String, Attributes> entries = manifest.getEntries();
for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
// Digest of the manifest stanza for this entry.
print.print("Name: " + entry.getKey() + "\r\n");
for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
print.print(att.getKey() + ": " + att.getValue() + "\r\n");
}
print.print("\r\n");
print.flush();
Attributes sfAttr = new Attributes();
sfAttr.putValue(hash == USE_SHA256 ? "SHA-256-Digest" : "SHA1-Digest-Manifest",
new String(Base64.encode(md.digest()), "ASCII"));
sf.getEntries().put(entry.getKey(), sfAttr);
}
CountOutputStream cout = new CountOutputStream(out);
sf.write(cout);
// A bug in the java.util.jar implementation of Android platforms
// up to version 1.6 will cause a spurious IOException to be thrown
// if the length of the signature file is a multiple of 1024 bytes.
// As a workaround, add an extra CRLF in this case.
if ((cout.size() % 1024) == 0) {
cout.write('\r');
cout.write('\n');
}
}
/** Sign data and write the digital signature to 'out'. */
private static void writeSignatureBlock(
CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey, OutputStream out)
throws IOException, CertificateEncodingException, OperatorCreationException, CMSException {
ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(1);
certList.add(publicKey);
JcaCertStore certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner signer = new JcaContentSignerBuilder(getSignatureAlgorithm(publicKey))
.setProvider(sBouncyCastleProvider)
.build(privateKey);
gen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder()
.setProvider(sBouncyCastleProvider)
.build())
.setDirectSignature(true)
.build(signer, publicKey));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(data, false);
ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
DEROutputStream dos = new DEROutputStream(out);
dos.writeObject(asn1.readObject());
}
/**
* Copy all the files in a manifest from input to output. We set
* the modification times in the output to a fixed time, so as to
* reduce variation in the output file and make incremental OTAs
* more efficient.
*/
private static void copyFiles(Manifest manifest,
JarMap in, JarOutputStream out, long timestamp) throws IOException {
Map<String, Attributes> entries = manifest.getEntries();
ArrayList<String> names = new ArrayList<>(entries.keySet());
Collections.sort(names);
for (String name : names) {
JarEntry inEntry = in.getJarEntry(name);
JarEntry outEntry;
if (inEntry.getMethod() == JarEntry.STORED) {
// Preserve the STORED method of the input entry.
outEntry = new JarEntry(inEntry);
} else {
// Create a new entry so that the compressed len is recomputed.
outEntry = new JarEntry(name);
}
outEntry.setTime(timestamp);
out.putNextEntry(outEntry);
in.getStream(name).writeTo(out);
out.flush();
}
}
private static class WholeFileSignerOutputStream extends FilterOutputStream {
private boolean closing = false;
private ByteArrayOutputStream footer = new ByteArrayOutputStream();
private OutputStream tee;
public WholeFileSignerOutputStream(OutputStream out, OutputStream tee) {
super(out);
this.tee = tee;
}
public void notifyClosing() {
closing = true;
}
public void finish() throws IOException {
closing = false;
byte[] data = footer.toByteArray();
if (data.length < 2)
throw new IOException("Less than two bytes written to footer");
write(data, 0, data.length - 2);
}
public byte[] getTail() {
return footer.toByteArray();
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (closing) {
// if the jar is about to close, save the footer that will be written
footer.write(b, off, len);
}
else {
// write to both output streams. out is the CMSTypedData signer and tee is the file.
out.write(b, off, len);
tee.write(b, off, len);
}
}
@Override
public void write(int b) throws IOException {
if (closing) {
// if the jar is about to close, save the footer that will be written
footer.write(b);
}
else {
// write to both output streams. out is the CMSTypedData signer and tee is the file.
out.write(b);
tee.write(b);
}
}
}
private static class CMSSigner implements CMSTypedData {
private JarMap inputJar;
private InputStream publicKeyFile;
private X509Certificate publicKey;
private PrivateKey privateKey;
private OutputStream outputStream;
private final ASN1ObjectIdentifier type;
private WholeFileSignerOutputStream signer;
public CMSSigner(JarMap inputJar, InputStream publicKeyFile,
X509Certificate publicKey, PrivateKey privateKey,
OutputStream outputStream) {
this.inputJar = inputJar;
this.publicKeyFile = publicKeyFile;
this.publicKey = publicKey;
this.privateKey = privateKey;
this.outputStream = outputStream;
this.type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
}
public Object getContent() {
// Not supported, but still don't crash or return null
return 1;
}
public ASN1ObjectIdentifier getContentType() {
return type;
}
public void write(OutputStream out) throws IOException {
try {
signer = new WholeFileSignerOutputStream(out, outputStream);
JarOutputStream outputJar = new JarOutputStream(signer);
int hash = getDigestAlgorithm(publicKey);
// Assume the certificate is valid for at least an hour.
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
Manifest manifest = addDigestsToManifest(inputJar, hash);
copyFiles(manifest, inputJar, outputJar, timestamp);
// Don't add Otacert, it's not an OTA
// addOtacert(outputJar, publicKeyFile, timestamp, manifest, hash);
signFile(manifest, inputJar, publicKey, privateKey, outputJar);
signer.notifyClosing();
outputJar.close();
signer.finish();
}
catch (Exception e) {
throw new IOException(e);
}
}
public void writeSignatureBlock(ByteArrayOutputStream temp)
throws IOException,
CertificateEncodingException,
OperatorCreationException,
CMSException {
ZipUtils.writeSignatureBlock(this, publicKey, privateKey, temp);
}
public WholeFileSignerOutputStream getSigner() {
return signer;
}
}
private static void signWholeFile(JarMap inputJar, InputStream publicKeyFile,
X509Certificate publicKey, PrivateKey privateKey,
OutputStream outputStream) throws Exception {
CMSSigner cmsOut = new CMSSigner(inputJar, publicKeyFile,
publicKey, privateKey, outputStream);
ByteArrayOutputStream temp = new ByteArrayOutputStream();
// put a readable message and a null char at the start of the
// archive comment, so that tools that display the comment
// (hopefully) show something sensible.
// TODO: anything more useful we can put in this message?
byte[] message = "signed by SignApk".getBytes("UTF-8");
temp.write(message);
temp.write(0);
cmsOut.writeSignatureBlock(temp);
byte[] zipData = cmsOut.getSigner().getTail();
// For a zip with no archive comment, the
// end-of-central-directory record will be 22 bytes long, so
// we expect to find the EOCD marker 22 bytes from the end.
if (zipData[zipData.length-22] != 0x50 ||
zipData[zipData.length-21] != 0x4b ||
zipData[zipData.length-20] != 0x05 ||
zipData[zipData.length-19] != 0x06) {
throw new IllegalArgumentException("zip data already has an archive comment");
}
int total_size = temp.size() + 6;
if (total_size > 0xffff) {
throw new IllegalArgumentException("signature is too big for ZIP file comment");
}
// signature starts this many bytes from the end of the file
int signature_start = total_size - message.length - 1;
temp.write(signature_start & 0xff);
temp.write((signature_start >> 8) & 0xff);
// Why the 0xff bytes? In a zip file with no archive comment,
// bytes [-6:-2] of the file are the little-endian offset from
// the start of the file to the central directory. So for the
// two high bytes to be 0xff 0xff, the archive would have to
// be nearly 4GB in size. So it's unlikely that a real
// commentless archive would have 0xffs here, and lets us tell
// an old signed archive from a new one.
temp.write(0xff);
temp.write(0xff);
temp.write(total_size & 0xff);
temp.write((total_size >> 8) & 0xff);
temp.flush();
// Signature verification checks that the EOCD header is the
// last such sequence in the file (to avoid minzip finding a
// fake EOCD appended after the signature in its scan). The
// odds of producing this sequence by chance are very low, but
// let's catch it here if it does.
byte[] b = temp.toByteArray();
for (int i = 0; i < b.length-3; ++i) {
if (b[i] == 0x50 && b[i+1] == 0x4b && b[i+2] == 0x05 && b[i+3] == 0x06) {
throw new IllegalArgumentException("found spurious EOCD header at " + i);
}
}
outputStream.write(total_size & 0xff);
outputStream.write((total_size >> 8) & 0xff);
temp.writeTo(outputStream);
}
private static void signFile(Manifest manifest, JarMap inputJar,
X509Certificate publicKey, PrivateKey privateKey,
JarOutputStream outputJar)
throws Exception {
// Assume the certificate is valid for at least an hour.
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
// MANIFEST.MF
JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
manifest.write(outputJar);
// CERT.SF / CERT#.SF
je = new JarEntry(CERT_SF_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeSignatureFile(manifest, baos, getDigestAlgorithm(publicKey));
byte[] signedData = baos.toByteArray();
outputJar.write(signedData);
// CERT.{DSA,EC,RSA} / CERT#.{DSA,EC,RSA}
je = new JarEntry((String.format(CERT_SIG_NAME, privateKey.getAlgorithm())));
je.setTime(timestamp);
outputJar.putNextEntry(je);
writeSignatureBlock(new CMSProcessableByteArray(signedData),
publicKey, privateKey, outputJar);
}
}

View File

@@ -1,307 +0,0 @@
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <zlib.h>
#include <android/log.h>
#define LOG_TAG "zipadjust"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
size_t insize, outsize = 0, alloc = 0;
unsigned char *fin, *fout;
int zipadjust(int decompress) ;
JNIEXPORT jbyteArray JNICALL
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust(JNIEnv *env, jclass type, jbyteArray jbytes,
jint size) {
fin = (*env)->GetPrimitiveArrayCritical(env, jbytes, NULL);
insize = (size_t) size;
zipadjust(0);
(*env)->ReleasePrimitiveArrayCritical(env, jbytes, fin, 0);
jbyteArray ret = (*env)->NewByteArray(env, outsize);
(*env)->SetByteArrayRegion(env, ret, 0, outsize, (const jbyte*) fout);
free(fout);
return ret;
}
#pragma pack(1)
struct local_header_struct {
uint32_t signature;
uint16_t extract_version;
uint16_t flags;
uint16_t compression_method;
uint16_t last_modified_time;
uint16_t last_modified_date;
uint32_t crc32;
uint32_t size_compressed;
uint32_t size_uncompressed;
uint16_t length_filename;
uint16_t length_extra;
// filename
// extra
};
typedef struct local_header_struct local_header_t;
#pragma pack(1)
struct data_descriptor_struct {
uint32_t signature;
uint32_t crc32;
uint32_t size_compressed;
uint32_t size_uncompressed;
};
typedef struct data_descriptor_struct data_descriptor_t;
#pragma pack(1)
struct central_header_struct {
uint32_t signature;
uint16_t version_made;
uint16_t version_needed;
uint16_t flags;
uint16_t compression_method;
uint16_t last_modified_time;
uint16_t last_modified_date;
uint32_t crc32;
uint32_t size_compressed;
uint32_t size_uncompressed;
uint16_t length_filename;
uint16_t length_extra;
uint16_t length_comment;
uint16_t disk_start;
uint16_t attr_internal;
uint32_t attr_external;
uint32_t offset;
// filename
// extra
// comment
};
typedef struct central_header_struct central_header_t;
#pragma pack(1)
struct central_footer_struct {
uint32_t signature;
uint16_t disk_number;
uint16_t disk_number_central_directory;
uint16_t central_directory_entries_this_disk;
uint16_t central_directory_entries_total;
uint32_t central_directory_size;
uint32_t central_directory_offset;
uint16_t length_comment;
// comment
};
typedef struct central_footer_struct central_footer_t;
#define MAGIC_LOCAL_HEADER 0x04034b50
#define MAGIC_DATA_DESCRIPTOR 0x08074b50
#define MAGIC_CENTRAL_HEADER 0x02014b50
#define MAGIC_CENTRAL_FOOTER 0x06054b50
static int xerror(char* message) {
LOGE("%s\n", message);
return 0;
}
static int xseekread(off_t offset, void* buf, size_t bytes) {
memcpy(buf, fin + offset, bytes);
return 1;
}
static int xseekwrite(off_t offset, const void* buf, size_t bytes) {
if (offset + bytes > outsize) outsize = offset + bytes;
if (outsize > alloc) {
fout = realloc(fout, outsize);
alloc = outsize;
}
memcpy(fout + offset, buf, bytes);
return 1;
}
static int xfilecopy(off_t offsetIn, off_t offsetOut, size_t bytes) {
unsigned int CHUNK = 256 * 1024;
unsigned char* buf = malloc(CHUNK);
if (buf == NULL) return xerror("malloc failed");
size_t left = bytes;
while (left > 0) {
size_t wanted = (left < CHUNK) ? left : CHUNK;
xseekread(offsetIn, buf, wanted);
xseekwrite(offsetOut, buf, wanted);
offsetIn += wanted;
offsetOut += wanted;
left -= wanted;
}
free(buf);
return 1;
}
static int xdecompress(off_t offsetIn, off_t offsetOut, size_t bytes) {
unsigned int CHUNK = 256 * 1024;
int ret;
unsigned have;
z_stream strm;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit2(&strm, -15);
if (ret != Z_OK) return xerror("ret != Z_OK");
do {
strm.avail_in = insize - offsetIn;
if (strm.avail_in == 0) break;
strm.avail_in = (strm.avail_in > CHUNK) ? CHUNK : strm.avail_in;
xseekread(offsetIn, in, strm.avail_in);
strm.next_in = in;
offsetIn += strm.avail_in;
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
if (ret == Z_STREAM_ERROR) return xerror("Stream error");
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return xerror("DICT/DATA/MEM error");
}
have = CHUNK - strm.avail_out;
xseekwrite(offsetOut, out, have);
offsetOut += have;
} while (strm.avail_out == 0);
} while (ret != Z_STREAM_END);
(void)inflateEnd(&strm);
return ret == Z_STREAM_END ? 1 : 0;
}
int zipadjust(int decompress) {
int ok = 0;
char filename[1024];
central_footer_t central_footer;
uint32_t central_directory_in_position = 0;
uint32_t central_directory_in_size = 0;
uint32_t central_directory_out_size = 0;
int i;
for (i = insize - 4; i >= 0; i--) {
uint32_t magic = 0;
if (!xseekread(i, &magic, sizeof(uint32_t))) return 0;
if (magic == MAGIC_CENTRAL_FOOTER) {
LOGD("central footer @ %08X\n", i);
if (!xseekread(i, &central_footer, sizeof(central_footer_t))) return 0;
central_header_t central_header;
if (!xseekread(central_footer.central_directory_offset, &central_header, sizeof(central_header_t))) return 0;
if ( central_header.signature == MAGIC_CENTRAL_HEADER ) {
central_directory_in_position = central_footer.central_directory_offset;
central_directory_in_size = insize - central_footer.central_directory_offset;
LOGD("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size);
break;
}
}
}
if (central_directory_in_position == 0) return 0;
unsigned char* central_directory_in = (unsigned char*)malloc(central_directory_in_size);
unsigned char* central_directory_out = (unsigned char*)malloc(central_directory_in_size);
if (!xseekread(central_directory_in_position, central_directory_in, central_directory_in_size)) return 0;
memset(central_directory_out, 0, central_directory_in_size);
fout = (unsigned char*) malloc(insize);
alloc = insize;
uintptr_t central_directory_in_index = 0;
uintptr_t central_directory_out_index = 0;
central_header_t* central_header = NULL;
uint32_t out_index = 0;
while (1) {
central_header = (central_header_t*)&central_directory_in[central_directory_in_index];
if (central_header->signature != MAGIC_CENTRAL_HEADER) break;
filename[central_header->length_filename] = (char)0;
memcpy(filename, &central_directory_in[central_directory_in_index + sizeof(central_header_t)], central_header->length_filename);
LOGD("%s (%d --> %d) [%08X] (%d)\n", filename, central_header->size_uncompressed, central_header->size_compressed, central_header->crc32, central_header->length_extra + central_header->length_comment);
local_header_t local_header;
if (!xseekread(central_header->offset, &local_header, sizeof(local_header_t))) return 0;
// save and update to next index before we clobber the data
uint16_t compression_method_old = central_header->compression_method;
uint32_t size_compressed_old = central_header->size_compressed;
uint32_t offset_old = central_header->offset;
uint32_t length_extra_old = central_header->length_extra;
central_directory_in_index += sizeof(central_header_t) + central_header->length_filename + central_header->length_extra + central_header->length_comment;
// copying, rewriting, and correcting local and central headers so all the information matches, and no data descriptors are necessary
central_header->offset = out_index;
central_header->flags = central_header->flags & !8;
if (decompress && (compression_method_old == 8)) {
central_header->compression_method = 0;
central_header->size_compressed = central_header->size_uncompressed;
}
central_header->length_extra = 0;
central_header->length_comment = 0;
local_header.compression_method = central_header->compression_method;
local_header.flags = central_header->flags;
local_header.crc32 = central_header->crc32;
local_header.size_uncompressed = central_header->size_uncompressed;
local_header.size_compressed = central_header->size_compressed;
local_header.length_extra = 0;
if (!xseekwrite(out_index, &local_header, sizeof(local_header_t))) return 0;
out_index += sizeof(local_header_t);
if (!xseekwrite(out_index, &filename[0], central_header->length_filename)) return 0;
out_index += central_header->length_filename;
if (decompress && (compression_method_old == 8)) {
if (!xdecompress(offset_old + sizeof(local_header_t) + central_header->length_filename + length_extra_old, out_index, size_compressed_old)) return 0;
} else {
if (!xfilecopy(offset_old + sizeof(local_header_t) + central_header->length_filename + length_extra_old, out_index, size_compressed_old)) return 0;
}
out_index += local_header.size_compressed;
memcpy(&central_directory_out[central_directory_out_index], central_header, sizeof(central_header_t) + central_header->length_filename);
central_directory_out_index += sizeof(central_header_t) + central_header->length_filename;
}
central_directory_out_size = central_directory_out_index;
central_footer.central_directory_size = central_directory_out_size;
central_footer.central_directory_offset = out_index;
central_footer.length_comment = 0;
if (!xseekwrite(out_index, central_directory_out, central_directory_out_size)) return 0;
out_index += central_directory_out_size;
if (!xseekwrite(out_index, &central_footer, sizeof(central_footer_t))) return 0;
LOGD("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size);
LOGD("central footer @ %08X\n", out_index);
ok = 1;
free(central_directory_in);
free(central_directory_out);
return ok;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,4 +0,0 @@
<vector android:height="24dp" android:viewportHeight="400.0"
android:viewportWidth="400.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="1.00" android:fillColor="#000000" android:pathData="M200.6,24.2C231.8,24.2 263,33 289.6,49.5C304.7,58.8 318.2,70.7 329.8,84.1C351.5,109.6 365.2,141.7 368.8,175C373.6,217.4 361.5,261.5 335.5,295.5C334.2,297.1 332.8,298.7 331.9,300.7C341.7,310.1 351,320 360.9,329.3C322.7,339.3 284.5,349.6 246.3,359.9C256.4,323.5 266,287 275.7,250.5C262.4,250.5 249.1,250.5 235.8,250.5C228.5,269.8 221.2,289.1 214.1,308.4C205.9,308.6 197.8,308.5 189.6,308.5C188.4,306.7 187.1,304.9 185.9,303C192.4,285.5 199.1,268 205.6,250.5C189.5,250.6 173.4,250.3 157.4,250.6C150.4,270 142.9,289.2 135.8,308.5C129.8,308.5 123.9,308.4 117.9,308.6C130.4,317.8 144,325.6 158.9,330.3C171.9,334.6 185.6,336.6 199.4,336.7C199.4,349.4 199.3,362.1 199.4,374.8C165.8,374.9 132.2,364.5 104.4,345.6C91.7,337 80.3,326.5 70.2,314.9C48.5,289.4 34.8,257.3 31.2,224C26.3,180.4 39.2,134.9 66.8,100.7C67.2,99.9 67.7,99.2 68.1,98.4C58.3,88.9 49,79 39.1,69.7C77.3,59.7 115.6,49.4 153.7,39.1C143.6,75.7 133.8,112.5 124.1,149.3C137.9,149.3 151.7,149.2 165.5,149.3C172.9,130 180.2,110.6 187.3,91.2C195.5,90.8 203.7,91 211.8,91C213,92.8 214.3,94.6 215.5,96.3C209.1,114 202.3,131.6 195.8,149.2C211.8,149.3 227.8,149.2 243.9,149.3C251.2,129.9 258.3,110.3 265.8,90.9C271.5,90.8 277.3,91.6 282.9,90.4C280.2,89.5 278.1,87.7 275.9,86.1C254,70.7 227.4,62.2 200.6,62.3C200.6,49.6 200.7,36.9 200.6,24.2M292.5,100C286.4,116.4 280.2,132.8 274,149.3C280.9,149.2 287.8,149.3 294.7,149.2C296,150.8 297.3,152.4 298.6,153.9C297.1,162.1 295.7,170.3 294.3,178.5C283.9,178.5 273.4,178.5 262.9,178.5C257.5,192.7 252.1,206.9 246.9,221.2C258.7,221.3 270.5,221.1 282.3,221.3C283.5,222.9 284.8,224.4 286.1,225.9C284.9,233.7 283.4,241.4 282.1,249.1C281.6,251 283.6,252.1 284.6,253.3C291.5,259.8 297.7,266.9 304.8,273.1C312.9,262.1 319.6,250.1 324.2,237.3C334.3,208.7 334.2,176.7 323.7,148.3C317,130.1 306.4,113.4 292.5,100M95.2,125.9C86.2,138 79,151.4 74.5,165.8C65.3,194.4 66.4,226.3 77.6,254.2C84.6,271.5 95.1,287.4 108.7,300.1C114.9,283.6 121.3,267.1 127.2,250.5C121.5,250.5 115.8,250.5 110,250.5C108.6,250.4 106.7,251 105.8,249.5C104.5,247.9 102.3,246.3 102.9,244.1C104.2,236.5 105.5,228.9 106.8,221.2C117.4,221.2 128.1,221.4 138.7,221.1C143.9,206.9 149.3,192.7 154.6,178.5C142.9,178.4 131.1,178.6 119.3,178.4C117.6,177.3 116.4,175.3 115,173.8C116.3,165.8 117.9,157.9 119,150C118.3,148.2 116.6,147.1 115.3,145.7C108.6,139.2 102.3,132.1 95.2,125.9M168.8,221.2C184.8,221.2 200.8,221.3 216.8,221.2C222.2,207 227.5,192.8 232.8,178.5C216.8,178.5 200.7,178.5 184.6,178.5C179.4,192.8 174,207 168.8,221.2Z"/>
</vector>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#000"
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#000"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View File

@@ -1,27 +0,0 @@
<vector android:height="24dp" android:viewportHeight="2230.0"
android:viewportWidth="2230.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M416,565"/>
<path android:fillColor="#FF000000" android:pathData="M1014,935c-238.9,170.6 -487.1,149.8 -601,-221 -20.2,-65.8 -63.9,478 186,584 74.2,31.5 34.8,-78.8 167,-179C840.5,1062.5 1060.7,901.7 1014,935Z"/>
<path android:fillColor="#FF000000" android:pathData="M982,1370c-64.6,63.2 -78.9,85.3 -162,158 -119.8,104.9 -241,-42 -223,-12 107.9,179.8 389.1,481.3 376,406 -26,-148.8 -46,-225 -28,-364C957.8,1458.7 1003.8,1348.7 982,1370Z"/>
<path android:fillColor="#FF000000" android:pathData="M1252,1359c64.6,63.2 78.9,85.3 162,158 119.8,104.9 241,-42 223,-12 -107.9,179.8 -389.1,481.3 -376,406 26,-148.8 46,-225 28,-364C1276.2,1447.7 1230.2,1337.7 1252,1359Z"/>
<path android:fillColor="#FF000000" android:pathData="M1073,1422"/>
<path android:fillColor="#FF000000" android:pathData="M782,1665"/>
<path android:fillColor="#FF000000" android:pathData="M899,1281"/>
<path android:fillColor="#FF000000" android:pathData="M946,884c-36.8,-163.5 -422.2,-66 -524,-325 -22.7,-57.7 30.6,415.1 319,427C880.7,991.8 953.2,916.1 946,884Z"/>
<path android:fillColor="#FF000000" android:pathData="M983,799C813.2,659.7 722.9,478 576,279c-27.7,-37.5 124,-128 184,-134S870.3,359.4 876,489C881.2,607.4 1005.8,817.7 983,799Z"/>
<path android:fillColor="#FF000000" android:pathData="M983,953l62,70"/>
<path android:fillColor="#FF000000" android:pathData="M445,825"/>
<path android:fillColor="#FF000000" android:pathData="M964,812S807.2,727.8 673,691C547.6,656.6 453.5,572.2 443,483c-7.8,-66.4 97,-192 97,-192s73.5,204 170,306C815,708 775.1,693 964,812Z"/>
<path android:fillColor="#FF000000" android:pathData="M1081,1055c26,10.2 14.6,-538.8 26.6,-685.8S1030,132 997,105s-96.1,-6.4 -172,19c85.9,58.9 129,129 141,225C948,570.9 1067,679 1081,1055Z"/>
<path android:fillColor="#FF000000" android:pathData="M980.3,1120.8c-131.5,37.9 -182.5,220.5 -315.2,243.1 -27.4,4.7 -58.3,12.9 -94.3,11.2 -67.3,-3.2 -152.4,-41.3 -266.1,-202.6 -33.6,-47.7 83.4,307.1 377,311.5 215,3.2 240.1,-210.5 383.4,-217 51.7,-2.3 55,-101 55,-101S1058.2,1098.4 980.3,1120.8Z"/>
<path android:fillColor="#FF000000" android:pathData="M1258.7,1121.8c131.5,37.9 182.5,220.5 315.2,243.1 27.4,4.7 58.3,12.9 94.3,11.2 67.3,-3.2 152.4,-41.3 266.1,-202.6 33.6,-47.7 -83.4,307.1 -377,311.5 -215,3.2 -240.1,-210.5 -383.4,-217 -51.7,-2.3 -55,-101 -55,-101S1180.8,1099.4 1258.7,1121.8Z"/>
<path android:fillColor="#FF000000" android:pathData="M512,1317"/>
<path android:fillColor="#FF000000" android:pathData="M1224,935c238.9,170.6 487.1,149.8 601,-221 20.2,-65.8 63.9,478 -186,584 -74.2,31.5 -34.8,-78.8 -167,-179C1397.5,1062.5 1177.3,901.7 1224,935Z"/>
<path android:fillColor="#FF000000" android:pathData="M1223.1,1362c6.4,-8.8 -23.6,56.9 -3,245 25.1,228.8 -118.7,583 -118.7,583s-105.4,-353.5 -84.8,-576c16.5,-177.5 3,-258 3,-258S1112.6,1514.7 1223.1,1362Z"/>
<path android:fillColor="#FF000000" android:pathData="M1292,884c36.8,-163.5 422.2,-66 524,-325 22.7,-57.7 -30.6,415.1 -319,427C1357.3,991.8 1284.8,916.1 1292,884Z"/>
<path android:fillColor="#FF000000" android:pathData="M1255,799c169.8,-139.3 260.1,-321 407,-520 27.7,-37.5 -124,-128 -184,-134s-110.3,214.4 -116,344C1356.8,607.4 1232.2,817.7 1255,799Z"/>
<path android:fillColor="#FF000000" android:pathData="M1255,953l-62,70"/>
<path android:fillColor="#FF000000" android:pathData="M1274,812s156.8,-84.3 291,-121c125.4,-34.4 219.5,-118.8 230,-208 7.8,-66.4 -97,-192 -97,-192s-73.6,204 -170,306C1423,708 1462.9,693 1274,812Z"/>
<path android:fillColor="#FF000000" android:pathData="M1157,1055c-26,10.2 -14.6,-538.8 -26.6,-685.8S1208,132 1241,105s96.1,-6.4 172,19c-85.9,58.9 -129,129 -141,225C1290,570.9 1171,679 1157,1055Z"/>
<path android:fillColor="#FF000000" android:pathData="M749,1800"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:windowBackground"
android:orientation="vertical">
<include layout="@layout/toolbar"/>
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>

View File

@@ -1,213 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:id="@+id/install_info_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
<TextView
android:id="@+id/install_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp"
android:textStyle="bold" />
<TextView
android:id="@+id/current_version_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp"
android:textStyle="bold" />
</LinearLayout>
</android.support.v7.widget.CardView>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:maxWidth="400dp"
android:minWidth="400dp">
<android.support.v7.widget.CardView
android:id="@+id/bootimage_card"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingBottom="10dp"
android:text="@string/boot_image_title"
android:textStyle="bold" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="15dp"
android:layout_marginStart="15dp">
<Spinner
android:layout_width="0dp"
android:layout_height="match_parent"
android:id="@+id/block_spinner"
android:layout_weight="1" />
<Button
android:text="@string/detect_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/detect_bootimage"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/install_option_card"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingBottom="10dp"
android:text="@string/advanced_settings_title"
android:textStyle="bold" />
<CheckBox
android:text="@string/keep_force_encryption"
android:id="@+id/keep_force_enc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="25dp"
android:layout_marginStart="25dp" />
<CheckBox
android:text="@string/keep_dm_verity"
android:id="@+id/keep_verity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="25dp"
android:layout_marginStart="25dp" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/flash_button"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginEnd="25dp"
android:layout_marginStart="25dp">
<ImageView
android:layout_width="50dp"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/ic_launcher"
android:id="@+id/imageView"
android:layout_weight="0"
android:layout_marginEnd="20dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/magiskify"
android:ems="10"
android:gravity="center"
android:layout_weight="1"
android:textStyle="bold"
android:textAllCaps="false"
android:textSize="20sp"
android:fontFamily="sans-serif" />
<FrameLayout
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_weight="0"
android:layout_marginStart="20dp">
</FrameLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</LinearLayout>

View File

@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="@dimen/card_vertical_margin"
android:layout_marginEnd="@dimen/card_horizontal_margin"
android:layout_marginStart="@dimen/card_horizontal_margin"
android:layout_marginTop="@dimen/card_vertical_margin"
style="?attr/cardStyle"
android:minHeight="?android:attr/listPreferredItemHeight"
card_view:cardCornerRadius="@dimen/card_corner_radius"
card_view:cardElevation="@dimen/card_elevation">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_gravity="center_vertical"
android:padding="@dimen/card_layout_padding">
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_icon"
android:layout_width="@dimen/card_appicon_size"
android:layout_height="@dimen/card_appicon_size"
android:layout_centerVertical="true"
android:scaleType="centerCrop"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignBottom="@+id/app_icon"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingEnd="@dimen/card_appicon_size"
android:paddingStart="65dp"
android:weightSum="1">
<TextView
android:id="@+id/app_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:paddingEnd="3dp"
android:paddingStart="3dp"
android:textStyle="bold"/>
<TextView
android:id="@+id/app_package"
android:layout_width="fill_parent"
android:layout_height="25dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:marqueeRepeatLimit="marquee_forever"
android:maxLines="1"
android:paddingEnd="3dp"
android:paddingStart="3dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true">
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:gravity="center"
android:src="@drawable/ic_menu_overflow_material"
android:checked="false" />
</LinearLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>

View File

@@ -1,151 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
style="?attr/cardStyle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="@dimen/card_vertical_margin"
android:layout_marginEnd="@dimen/card_horizontal_margin"
android:layout_marginStart="@dimen/card_horizontal_margin"
android:layout_marginTop="@dimen/card_vertical_margin"
android:minHeight="?android:attr/listPreferredItemHeight"
card_view:cardCornerRadius="@dimen/card_corner_radius"
card_view:cardElevation="@dimen/card_elevation">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:padding="@dimen/card_layout_padding">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:id="@+id/info_layout">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="false"/>
<TextView
android:id="@+id/version_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false"
android:textStyle="bold|italic"/>
<TextView
android:id="@+id/author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false"
android:textStyle="bold|italic"/>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textIsSelectable="false" />
</LinearLayout>
<ImageView
android:id="@+id/update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@drawable/ic_file_download_black"
android:backgroundTint="@color/icon_grey"
android:focusable="false"
android:gravity="end" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/expand_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/info_layout"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/changeLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp"
android:src="@drawable/ic_changelog"
android:tint="@color/icon_grey"/>
<ImageView
android:id="@+id/authorLink"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp"
android:src="@drawable/ic_person"
android:tint="@color/icon_grey"/>
<ImageView
android:id="@+id/supportLink"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/card_imageview_margin"
android:layout_marginStart="@dimen/card_imageview_margin"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp"
android:src="@drawable/ic_help"
android:tint="@color/icon_grey"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>

View File

@@ -1,197 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:id="@+id/magiskStatusView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/magisk_version"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="6dp"
android:textStyle="bold"/>
<FrameLayout
android:id="@+id/magisk_status_container"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="?android:attr/selectableItemBackground"
android:foregroundGravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/magisk_status_icon"
android:layout_width="84dp"
android:layout_height="84dp"
android:layout_gravity="center"/>
<ProgressBar
android:id="@+id/magisk_check_updates_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
<TextView
android:id="@+id/magisk_update_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="6dp"
android:textStyle="bold"
android:text="@string/checking_for_updates" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/rootStatusView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/root_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="6dp"
android:textStyle="bold"/>
<FrameLayout
android:id="@+id/root_status_container"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="?android:attr/selectableItemBackground"
android:foregroundGravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/root_status_icon"
android:layout_width="84dp"
android:layout_height="84dp"
android:layout_gravity="center" />
</FrameLayout>
<TextView
android:id="@+id/root_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="6dp"
android:textStyle="bold"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/safetyNetView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:id="@+id/safetyNet_container"
android:layout_width="match_parent"
android:layout_height="100dp"
android:foregroundGravity="center"
android:orientation="vertical"
android:background="@color/grey500">
<ImageView
android:id="@+id/safetyNet_icon"
android:layout_width="84dp"
android:layout_height="84dp"
android:layout_gravity="center"
android:src="@drawable/ic_safetynet"/>
<ProgressBar
android:id="@+id/safetyNet_check_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
</FrameLayout>
<TextView
android:id="@+id/safetyNet_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="6dp"
android:textStyle="bold"
android:text="@string/safetyNet_check_text" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,126 +0,0 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<string name="navigation_drawer_open">فتح درج التنقل</string>
<string name="navigation_drawer_close">إغلاق درج التنقل</string>
<string name="magiskhide">Magisk إخفاء</string>
<string name="modules">الإضافات</string>
<string name="downloads">التنزيلات</string>
<string name="log">السجل</string>
<string name="settings">الإعدادات</string>
<!--Magisk Fragment-->
<string name="magisk_version">المثبت Magisk v%1$s</string>
<string name="magisk_version_error">هل قمت بتثبيت Magisk؟</string>
<string name="magisk_update_available">Magisk v%1$.1f تحديث!</string>
<string name="cannot_check_updates">لا يمكن التحقق من التحديثات</string>
<string name="up_to_date">أحدث إصدار من %1$s مثبت</string>
<!--Root Fragment-->
<!--Module Fragment-->
<string name="no_info_provided">(لم يتم توفير أي معلومات)</string>
<string name="no_modules_found">لم يعثر على الإضافات</string>
<string name="update_file_created">سيتم تحديث الإضافة في إعادة التشغيل التالي</string>
<string name="remove_file_created">سيتم حذف الإضافة في إعادة التشغيل التالي</string>
<string name="remove_file_deleted">لن يتم حذف الإضافة في إعادة التشغيل التالي</string>
<string name="disable_file_created">سيتم تعطيل الإضافة في إعادة التشغيل التالي</string>
<string name="disable_file_removed">سيتم تمكين الإضافة في إعادة التشغيل التالي</string>
<string name="author">إنشئ بواسطة %1$s</string>
<string name="fab_flash_zip">تثبيت الملف المضغوط للإضافة</string>
<!--Repo Fragment-->
<string name="update_available">يتوفر تحديث</string>
<string name="installed">مثبت</string>
<string name="not_installed">غير مثبت</string>
<string name="changelog">التغييرات</string>
<!--Log Fragment-->
<string name="menuSaveToSd">حفظ إلى بطاقة ذاكرة SD</string>
<string name="menuSend">إرسال</string>
<string name="menuReload">إعادة تحميل</string>
<string name="menuClearLog">حذف السجل الآن</string>
<string name="logs_cleared">تم حذف السجل بنجاح</string>
<string name="log_is_empty">السجل فارغ</string>
<string name="logs_save_failed">لا يمكن كتابة السجل على بطاقة ذاكرة SD:</string>
<!--About Activity-->
<string name="about">حول</string>
<string name="app_developers">المطورين الرئيسيين</string>
<string name="app_developers_"><![CDATA[التطبيق إنشئ بواسطة <a href="https://github.com/topjohnwu">topjohnwu</a> بالتعاون مع <a href="https://github.com/d8ahazard">Digitalhigh</a> و <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">تغييرات التطبيق</string>
<string name="translators">xx6600xx ,silent_6600</string>
<string name="app_version">إصدار التطبيق</string>
<string name="app_source_code">الشفرة المصدرية</string>
<string name="donation">التبرع</string>
<string name="app_translators">مترجم التطبيق</string>
<string name="support_thread">منتدى الدعم</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">أن هذه الميزة لا تعمل دون الحصول على إذن الكتابة على التخزين الخارجي.</string>
<string name="no_thanks">لا شكراً</string>
<string name="repo_install_title">تثبيت %1$s</string>
<string name="repo_install_msg">هل تريد تثبيت %1$s ?</string>
<string name="download_install">تنزيل وتثبيت</string>
<string name="download_file_error">خطأ تنزيل الملف</string>
<string name="install_error">خطأ في التثبيت!</string>
<string name="manual_install_1">الملف المضغوط وضع في %1$s</string>
<string name="manual_install_2">التثبيت في الإسترداد يدوياً</string>
<string name="invalid_zip">الملف المضغوط ليس إضافة Magisk!!</string>
<string name="reboot_title">التثبيت نجح!</string>
<string name="reboot_msg">هل تريد إعادة التشغيل الآن؟</string>
<string name="reboot">إعادة التشغيل</string>
<string name="zip_install_progress_title">تثبيت</string>
<string name="zip_install_unzip_zip_msg">فك الضغط عن الملف المضغوط …</string>
<string name="zip_install_process_zip_msg">معالجة الملف المضغوط …</string>
<string name="zip_install_progress_msg">"تثبيت %1$s …"</string>
<string name="no_magisk_title">لا يوجد Magisk مثبت!</string>
<string name="no_magisk_msg">هل ترغب في تنزيل وتثبيت Magisk؟</string>
<!--URL Templates-->
<!--Settings Activity -->
<string name="settings_general_category">عام</string>
<string name="settings_theme_title">السمة</string>
<string name="settings_theme_summary">أختر سمة</string>
<string name="theme_default">إفتراضي</string>
<string name="theme_dark">غامق</string>
<string name="settings_magiskhide_title">تمكين إخفاء Magisk</string>
<string name="settings_magiskhide_summary">إخفاء Magisk من مختلف حالات الإكتشاف</string>
<string name="settings_busybox_title">تمكين BusyBox</string>
<string name="settings_busybox_summary">ربط تحميل Magisk\'s المدمج في busybox إلى xbin</string>
<string name="settings_hosts_title">تمكين المضيفين(الهوست) لـ systemless</string>
<string name="settings_hosts_summary">Systemless يدعم تطبيقات حجب الإعلانات</string>
<string name="settings_development_category">التطوير</string>
<string name="settings_developer_logging_title">تمكين تصحيح السجلات المتقدمة</string>
<string name="settings_developer_logging_summary">حدد هذا الخيار لتمكين سجل مطول أكثر.</string>
<string name="settings_shell_logging_title">تمكين سجل تصحيح الأوامر الدفعية</string>
<string name="settings_shell_logging_summary">حدد هذا الخيار لتمكين سجل جميع الأوامر الدفعية والمخرجات</string>
<string name="settings_reboot_toast">إعادة التشغيل لتطبيق الإعدادات</string>
<string name="auto_detect">\"(تلقائي) %1$s\"</string>
<string name="checking_for_updates">البحث عن تحديثات…</string>
<string name="install">التثبيت</string>
<string name="not_rooted">غير مروت</string>
<string name="proper_root">مروت فعلاً</string>
<string name="advanced_settings_title">إعدادات متقدمة</string>
<string name="boot_image_title">موقع ملف الإقلاع</string>
<string name="checking_safetyNet_status">التحقق من حالة SafetyNet...</string>
<string name="copying_msg">نسخ الملف المضغوط إلى دليل مؤقت</string>
<string name="detect_button">تحقق</string>
<string name="downloading_toast">جاري التنزيل %1$s</string>
<string name="install_magisk_title">تثبيت Magisk الإصدار: v%1$.1f</string>
<string name="keep_force_encryption">إبقاء التشفير القوى</string>
<string name="keep_dm_verity">إبقاء dm-verity</string>
<string name="root_error">مروت لكن لا يوجد إذن الروت، غير مسموح به؟</string>
<string name="root_info_warning">وظائف محدودة إلى حد كبير</string>
<string name="safetyNet_error">تعذر التحقق من SafetyNet، لا يوجد إنترنت؟</string>
<string name="safetyNet_pass">SafetyNet تخطى</string>
<string name="safetyNet_fail">فشل SafetyNet: عدم تطابق التشكيل الجانبي CTS</string>
<string name="status">الحالة</string>
</resources>

View File

@@ -1,105 +0,0 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<string name="navigation_drawer_open">Navigationsleiste öffnen</string>
<string name="navigation_drawer_close">Navigationsleiste schließen</string>
<string name="modules">Module</string>
<string name="downloads">Downloads</string>
<string name="log">Log</string>
<string name="settings">Einstellungen</string>
<!--Magisk Fragment-->
<string name="magisk_version">Magisk v%1$s installiert</string>
<string name="magisk_version_error">Haben Sie Magisk installiert?</string>
<string name="magisk_update_available">Magisk v%1$.1f update!</string>
<string name="cannot_check_updates">Kann nicht auf Aktualisierungen prüfen</string>
<string name="up_to_date">Aktuellste Version von %1$s installiert</string>
<!--Root Fragment-->
<!--Module Fragment-->
<string name="no_info_provided">(Nichts angegeben)</string>
<string name="no_modules_found">Keine Module gefunden</string>
<string name="update_file_created">Modul wird beim nächsten Neustart aktualisiert</string>
<string name="remove_file_created">Modul wird beim nächsten Neustart entfernt</string>
<string name="remove_file_deleted">Modul wird beim nächsten Neustart nicht entfernt</string>
<string name="disable_file_created">Modul wird beim nächsten Neustart deaktiviert</string>
<string name="disable_file_removed">Modul wird beim nächsten Neustart aktiviert</string>
<string name="author">Erstellt von %1$s</string>
<!--Repo Fragment-->
<string name="update_available">Aktualisierung verfügbar</string>
<string name="installed">Installiert</string>
<string name="not_installed">Nicht Installiert</string>
<string name="changelog">Changelog</string>
<!--Log Fragment-->
<string name="menuSaveToSd">Auf SD Karte speichern</string>
<string name="menuSend">Senden</string>
<string name="menuReload">erneut laden</string>
<string name="menuClearLog">Log jetzt löschen</string>
<string name="logs_cleared">Log erfolgreich gelöscht</string>
<string name="log_is_empty">Log ist leer</string>
<string name="logs_save_failed">Konnte Log nicht auf SD Karte speichern:</string>
<!--About Activity-->
<string name="about">Über</string>
<string name="app_developers">Entwickler</string>
<string name="app_developers_"><![CDATA[Anwendung entwickelt von <a href="https://github.com/topjohnwu">topjohnwu</a> in Zusammenarbeit mit <a href="https://github.com/d8ahazard">Digitalhigh</a> und <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Anwendungs changelog</string>
<string name="translators">skalnet</string>
<string name="app_version">Anwendungs Version</string>
<string name="app_source_code">Quelltext</string>
<string name="donation">Spende</string>
<string name="app_translators">Anwendungs Übersetzer</string>
<string name="support_thread">Support thread</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">Diese Funktion wird ohne erlaubnis auf den externen Speicher zu schreiben nicht funktionieren.</string>
<string name="no_thanks">Nein Danke</string>
<string name="repo_install_title">Installiere %1$s</string>
<string name="repo_install_msg">Wollen Sie %1$s installieren?</string>
<string name="download_install">Herunterladen und Installieren</string>
<string name="download_file_error">Fehler beim Herunterladen</string>
<string name="install_error">Fehler beim Installieren!</string>
<string name="manual_install_1">Fehler beim Flashen. Datei in %1$s abgelegt\n Bitte in recovery manuell flashen</string>
<string name="invalid_zip">Diese Datei ist kein Magisk Modul!!</string>
<string name="reboot_title">Installation erfolgreich!</string>
<string name="reboot_msg">Wollen Sie jetzt neu starten?</string>
<string name="reboot">Neustart</string>
<string name="zip_install_progress_title">Installiere</string>
<string name="zip_install_progress_msg">"Installiere %1$s …"</string>
<string name="no_magisk_title">Kein Magisk Installiert!</string>
<string name="no_magisk_msg">Wollen Sie Magisk herunterladen und installieren?</string>
<!--URL Templates-->
<!--Settings Fragment -->
<string name="settings_general_category">Allgemein</string>
<string name="settings_theme_title">Aussehen</string>
<string name="settings_theme_summary">Wähle ein Theme aus</string>
<string name="theme_default">Standard</string>
<string name="theme_dark">Dunkel</string>
<string name="settings_busybox_title">BusyBox aktivieren</string>
<string name="settings_busybox_summary">Magisks eingebautes BusyBox zum PATH hinzufügen</string>
<string name="settings_development_category">Entwicklung</string>
<string name="settings_developer_logging_title">Aktiviere erweitertes logging zum debuggen</string>
<string name="settings_developer_logging_summary">Aktivieren um ausführlichere logs zu erhalten.</string>
<string name="settings_shell_logging_title">Aktiviere debug logging für Shell Befehle</string>
<string name="settings_shell_logging_summary">Aktivieren um alle Shell Befehle und Ausgaben zu loggen.</string>
<string name="settings_magiskhide_title">Aktiviere Magisk Hide</string>
<string name="settings_magiskhide_summary">Neu starten um Einstellungen zu aktivieren</string>
<!-- Strings related to Settings -->
<!-- Example General settings -->
<!-- Example settings for Data & Sync -->
<!-- Example settings for Notifications -->
</resources>

View File

@@ -1,103 +0,0 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<string name="navigation_drawer_open">Abrir menú lateral</string>
<string name="navigation_drawer_close">Cerrar menú lateral</string>
<string name="magiskhide">Magisk Hide</string>
<string name="modules">Modulos</string>
<string name="downloads">Descargas</string>
<string name="log">Log</string>
<string name="settings">Ajustes</string>
<!--Magisk Fragment-->
<string name="magisk_version">Instalado Magisk v%1$s</string>
<string name="magisk_version_error">¿Has instalado Magisk?</string>
<string name="magisk_update_available">¡Actualización Magisk v%1$.1f!</string>
<string name="cannot_check_updates">No se pueden buscar actualizaciones</string>
<string name="up_to_date">Última versión de %1$s instalada</string>
<!--Root Fragment-->
<!--Module Fragment-->
<string name="no_info_provided">(No hay información)</string>
<string name="no_modules_found">No se han encontrado módulos</string>
<string name="update_file_created">El módulo se actualizará en el siguiente reinicio</string>
<string name="remove_file_created">El módulo se eliminará en el siguiente reinicio</string>
<string name="remove_file_deleted">El módulo no se eliminirá en el siguiente reinicio</string>
<string name="disable_file_created">El módulo se desactivará en el siguiente reinicio</string>
<string name="disable_file_removed">El móodulo se activará en el siguiente reinicio</string>
<string name="author">Creado por %1$s</string>
<string name="fab_flash_zip">Flashear Módulo Zip</string>
<!--Repo Fragment-->
<string name="update_available">Actualización disponible</string>
<string name="installed">Instalado</string>
<string name="not_installed">No Instalado</string>
<string name="changelog">Cambios</string>
<!--Log Fragment-->
<string name="menuSaveToSd">Salvar a SD</string>
<string name="menuSend">Enviar</string>
<string name="menuReload">Recargar</string>
<string name="menuClearLog">Vaciar Log ahora</string>
<string name="logs_cleared">Log vaciado correctamente</string>
<string name="log_is_empty">El Log está vacio</string>
<string name="logs_save_failed">No se ha podido escribir el log en la tarjeta SD:</string>
<!--About Activity-->
<string name="about">Acerca de</string>
<string name="app_developers">Desarroladores principales</string>
<string name="app_developers_"><![CDATA[App created by <a href="https://github.com/topjohnwu">topjohnwu</a> in collaboration with <a href="https://github.com/d8ahazard">Digitalhigh</a> and <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Cambios en la aplicación</string>
<string name="translators">Gawenda, netizen</string>
<string name="app_version">Versión de la aplicación</string>
<string name="app_source_code">Código fuente</string>
<string name="donation">Donar</string>
<string name="app_translators">Traductores de la aplicación</string>
<string name="support_thread">Hilo de soporte</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">Esta opción no funcionará sin permiso de escritura en la memoria externa</string>
<string name="no_thanks">No gracias</string>
<string name="repo_install_title">Instalar %1$s</string>
<string name="repo_install_msg">¿ Quieres instalar %1$s ?</string>
<string name="download_install">Descargar e instalar</string>
<string name="download_file_error">Error descargando fichero</string>
<string name="install_error">¡Error en la instalación!</string>
<string name="manual_install_1">Error flasheando fichero, zip colocado en %1$s\nFlashear manualmente desde recovery</string>
<string name="invalid_zip">¡El zip no es un Módulo Magisk!</string>
<string name="reboot_title">¡Instalación correcta!</string>
<string name="reboot_msg">¿Deseas reiniciar ahora?</string>
<string name="reboot">Reinciar</string>
<string name="zip_install_progress_title">Instalando</string>
<string name="zip_install_progress_msg">"Instalando %1$s …"</string>
<string name="no_magisk_title">¡Magisk no instalado!</string>
<string name="no_magisk_msg">¿Deseas descargar e instalar Magisk?</string>
<!--URL Templates-->
<!--Settings Activity -->
<string name="settings_general_category">General</string>
<string name="settings_theme_title">Tema</string>
<string name="settings_theme_summary">Selecciona un tema</string>
<string name="theme_default">Por defecto</string>
<string name="theme_dark">Oscuro</string>
<string name="settings_magiskhide_title">Habilitar Magisk Hide</string>
<string name="settings_magiskhide_summary">Ocultar Magisk de varias detecciones</string>
<string name="settings_busybox_title">Habilitar BusyBox</string>
<string name="settings_busybox_summary">Montar el busybox interno de Magisk en xbin</string>
<string name="settings_hosts_title">Habilitar fichero hosts fuera de la partición de sistema</string>
<string name="settings_hosts_summary">Soporte para aplicaciones de bloqueo de publicidad fuera de la partición de sistema</string>
<string name="settings_development_category">Desarrollo</string>
<string name="settings_developer_logging_title">Habilitar información de depuración en el log</string>
<string name="settings_developer_logging_summary">Activar esto para grabar más información en el log</string>
<string name="settings_shell_logging_title">Grabar comandos de terminal en el log</string>
<string name="settings_shell_logging_summary">Activar esto para grabar en el log todos los comandos ejecutados y su resultado</string>
<string name="settings_reboot_toast">Reiniciar para aplicar ajustes</string>
</resources>

View File

@@ -1,103 +0,0 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<string name="navigation_drawer_open">Apri drawer di navigazione</string>
<string name="navigation_drawer_close">Chiudi drawer di navigazione</string>
<string name="modules">Moduli</string>
<string name="downloads">Downloads</string>
<string name="log">Log</string>
<string name="settings">Impostazioni</string>
<!--Magisk Fragment-->
<string name="magisk_version">Versione Magisk: v%1$s</string>
<string name="magisk_version_error">Hai installato Magisk?</string>
<string name="magisk_update_available">Magisk v%1$.1f update!</string>
<string name="cannot_check_updates">Impossibile controllare aggiornamenti</string>
<string name="up_to_date">L\'ultima versione di %1$s è installata</string>
<!--Root Fragment-->
<!--Module Fragment-->
<string name="no_info_provided">(Nessuna informazione)</string>
<string name="no_modules_found">Nessun modulo trovato</string>
<string name="update_file_created">Il modulo sarà aggiornato al prossimo riavvio</string>
<string name="remove_file_created">Il modulo sarà rimosso al prossimo riavvio</string>
<string name="remove_file_deleted">Il modulo non sarà rimosso al prossimo riavvio</string>
<string name="disable_file_created">Il modulo sarà disattivato al prossimo riavvio</string>
<string name="disable_file_removed">Il modulo sarà abilitato al prossimo riavvio</string>
<string name="author">Creato da: %1$s</string>
<!--Repo Fragment-->
<string name="update_available">Aggiornamento disponibile</string>
<string name="installed">Installato</string>
<string name="not_installed">Non installato</string>
<string name="changelog">Changelog</string>
<!--Log Fragment-->
<string name="menuSaveToSd">Salva nella SD</string>
<string name="menuSend">Invia</string>
<string name="menuReload">Ricarica</string>
<string name="menuClearLog">Cancella log</string>
<string name="logs_cleared">Log creato con successo</string>
<string name="log_is_empty">Il log è vuoto</string>
<string name="logs_save_failed">Impossibile scrivere il log sulla SD</string>
<!--About Activity-->
<string name="about">Informazioni</string>
<string name="app_developers">Sviluppatori</string>
<string name="app_developers_"><![CDATA[App creata da <a href="https://github.com/topjohnwu">topjohnwu</a> in collaborazione con <a href="https://github.com/d8ahazard">Digitalhigh</a> e <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Changelog</string>
<string name="translators">Fabb2303</string>
<string name="app_version">Versione App</string>
<string name="app_source_code">Codice sorgente</string>
<string name="donation">Donazione</string>
<string name="app_translators">Traduttori App</string>
<string name="support_thread">Thread di supporto</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">Questa funzione non funziona senza il permesso di scrivere sulla memoria di archiviazione esterna</string>
<string name="no_thanks">No grazie</string>
<string name="repo_install_title">Installazione %1$s</string>
<string name="repo_install_msg">Vuoi installare %1$s ?</string>
<string name="download_install">Scarica e installa</string>
<string name="download_file_error">Errore nel download del file</string>
<string name="install_error">Errore di installazione!</string>
<string name="manual_install_1">Errore nel flash del file, il file zip è in %1$s\nFlash esegui il flash manuale</string>
<string name="invalid_zip">Lo zip non è un Modulo Magisk!!</string>
<string name="reboot_title">Installazione completata</string>
<string name="reboot_msg">Vuoi riavviare ora?</string>
<string name="reboot">Riavvia</string>
<string name="zip_install_progress_title">Installazione</string>
<string name="zip_install_progress_msg">"Installazione %1$s …"</string>
<string name="no_magisk_title">Magisk non installato!</string>
<string name="no_magisk_msg">Vuoi scaricare ed installare Magisk?</string>
<!--URL Templates-->
<!--Settings Fragment -->
<string name="settings_general_category">Generali</string>
<string name="settings_theme_title">Tema</string>
<string name="settings_theme_summary">Scegli un tema</string>
<string name="theme_default">Default</string>
<string name="theme_dark">Scuro</string>
<string name="settings_busybox_title">Abilita BusyBox</string>
<string name="settings_busybox_summary">Make Magisk\'s built-in BusyBox be visible in PATH</string>
<string name="settings_development_category">Development</string>
<string name="settings_developer_logging_title">Abilita Debug log avanzato</string>
<string name="settings_developer_logging_summary">Abilita questa funzione per avere un log più dettagliato.</string>
<string name="settings_shell_logging_title">Abilita shell di registrazione dei comandi di debug</string>
<string name="settings_shell_logging_summary">Abilita questa funzione per abilitare la registrazione tutti i comandi e l\'output della shell</string>
<!-- Strings related to Settings -->
<!-- Example General settings -->
<!-- Example settings for Data & Sync -->
<!-- Example settings for Notifications -->
</resources>

View File

@@ -1,105 +0,0 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<string name="navigation_drawer_open">Open navigatie</string>
<string name="navigation_drawer_close">Sluit navigatie</string>
<string name="modules">Modules</string>
<string name="downloads">Downloads</string>
<string name="log">Log</string>
<string name="settings">Instellingen</string>
<!--Magisk Fragment-->
<string name="magisk_version">Geïnstalleerde Magisk: v%1$s</string>
<string name="magisk_version_error">Heb jij Magisk wel geïnstalleerd?</string>
<string name="magisk_update_available">Magisk v%1$.1f update!</string>
<string name="cannot_check_updates">Kan niet controleren op updates.</string>
<string name="up_to_date">Nieuwste versie van %1$s geïnstalleerd</string>
<!--Root Fragment-->
<!--Module Fragment-->
<string name="no_info_provided">(Geen informatie gegeven)</string>
<string name="no_modules_found">Geen modules gevonden</string>
<string name="update_file_created">Module wordt geüpdatet bij de volgende reboot.</string>
<string name="remove_file_created">Module wordt verwijderd bij de volgende reboot.</string>
<string name="remove_file_deleted">Module wordt niet verwijderd bij de volgende reboot.</string>
<string name="disable_file_created">Module wordt uitgeschakeld bij de volgende reboot.</string>
<string name="disable_file_removed">Module wordt ingeschakeld bij de volgende reboot.</string>
<string name="author">Gemaakt door: %1$s</string>
<!--Repo Fragment-->
<string name="update_available">Update Beschikbaar</string>
<string name="installed">Geïnstalleerd</string>
<string name="not_installed">Niet geïnstalleerd</string>
<string name="changelog">Changelog</string>
<!--Log Fragment-->
<string name="menuSaveToSd">Opslaan op SD-kaart</string>
<string name="menuSend">Verzenden</string>
<string name="menuReload">Herladen</string>
<string name="menuClearLog">Maak log leeg</string>
<string name="logs_cleared">Log succesvol geleegd</string>
<string name="log_is_empty">Log is leeg</string>
<string name="logs_save_failed">Kon niet naar SD-kaart schrijven:</string>
<!--About Activity-->
<string name="about">Over</string>
<string name="app_developers">Hoofddevelopers</string>
<string name="app_developers_"><![CDATA[App gemaakt door <a href="https://github.com/topjohnwu">topjohnwu</a> in samenwerking met <a href="https://github.com/d8ahazard">Digitalhigh</a> en <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">App\'s changelog</string>
<string name="translators">NaamloosDT, Klaessen</string>
<string name="app_version">App\'s versie</string>
<string name="app_source_code">Source code</string>
<string name="donation">Donatie</string>
<string name="app_translators">App\'s vertalers</string>
<string name="support_thread">Support thread</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">Deze feature zal niet werken zonder permissie om op de externe opslag te schrijven.</string>
<string name="no_thanks">Nee bedankt</string>
<string name="repo_install_title">Installeer %1$s</string>
<string name="repo_install_msg">Wilt u %1$s installeren?</string>
<string name="download_install">Downloaden en installeren</string>
<string name="download_file_error">Er is een fout opgetreden bij het downloaden van het bestand</string>
<string name="install_error">Er is een fout opgetreden in de installatie</string>
<string name="manual_install_1">Er is een fout opgetreden in het flashen van het bestand. Het zip bestand is opgeslagen in %1$s\nFlash het handmatig in recovery modus.</string>
<string name="invalid_zip">Zip bestand is geen Magisk Module!</string>
<string name="reboot_title">Installatie succesvol!</string>
<string name="reboot_msg">Wilt u nu rebooten?</string>
<string name="reboot">Reboot</string>
<string name="zip_install_progress_title">Installeren</string>
<string name="zip_install_progress_msg">"Instalatievoortgang: %1$s …"</string>
<string name="no_magisk_title">Geen Magisk geïnstalleerd!</string>
<string name="no_magisk_msg">Wilt u Magisk downloaden en installeren?</string>
<!--URL Templates-->
<!--Settings Fragment -->
<string name="settings_general_category">Algemeen</string>
<string name="settings_theme_title">Thema</string>
<string name="settings_theme_summary">Selecteer een thema</string>
<string name="theme_default">Standaard</string>
<string name="theme_dark">Donker</string>
<string name="settings_busybox_title">Schakel BusyBox in</string>
<string name="settings_busybox_summary">Maakt Magisk\'s ingebouwde BusyBox zichtbaar in PATH</string>
<string name="settings_development_category">Development</string>
<string name="settings_developer_logging_title">Geavanceerde debug logging</string>
<string name="settings_developer_logging_summary">Schakel dit in voor uitgebreidere logging.</string>
<string name="settings_shell_logging_title">Shell command debug loggin</string>
<string name="settings_shell_logging_summary">Schakel dit in om alle shell commands en output te loggen</string>
<string name="settings_magiskhide_title">Magisk verbergen</string>
<string name="settings_magiskhide_summary">Reboot om de instellingen toe te passen</string>
<!-- Strings related to Settings -->
<!-- Example General settings -->
<!-- Example settings for Data & Sync -->
<!-- Example settings for Notifications -->
</resources>

View File

@@ -1,130 +0,0 @@
<resources>
<!--Welcome Activity-->
<string name="navigation_drawer_open">Abrir gaveta de notificação</string>
<string name="navigation_drawer_close">Fechar gaveta de notificação</string>
<string name="magiskhide">Magisk Hide</string>
<string name="modules">Módulos</string>
<string name="downloads">Baixar</string>
<string name="log">Registro</string>
<string name="settings">Configurações</string>
<string name="status">Status</string>
<string name="install">Instalar</string>
<!--Status Fragment-->
<string name="magisk_version">Magisk v%1$s Instalado</string>
<string name="magisk_version_error">Magisk não instalado</string>
<string name="checking_for_updates">Checando por atualizações…</string>
<string name="magisk_update_available">Magisk v%1$.1f disponível!</string>
<string name="cannot_check_updates">Não é possível verificar se há atualizações</string>
<string name="up_to_date">Última versão do %1$s instalado</string>
<string name="root_error">Rooteado mas sem permissão de root, o acesso foi permitido?</string>
<string name="not_rooted">Sem root</string>
<string name="proper_root">Rooteado</string>
<string name="safetyNet_check_text">Pressione para checar o SafetyNet</string>
<string name="checking_safetyNet_status">Checando status do SafetyNet…</string>
<string name="safetyNet_connection_failed">Não é possível conectar-se à API do Google</string>
<string name="safetyNet_connection_suspended">A conexão com API do Google foi suspensa</string>
<string name="safetyNet_error">Não é possível verificar o SafetyNet, sem Internet?</string>
<string name="safetyNet_fail">SafetyNet Falhou: CTS profile mismatch</string>
<string name="safetyNet_pass">SafetyNet Passado</string>
<string name="root_info_warning">Funcionalidade muito limitada</string>
<!--Install Fragment-->
<string name="auto_detect">"(Auto) %1$s"</string>
<string name="boot_image_title">Local da Boot Image</string>
<string name="detect_button">Detectar</string>
<string name="advanced_settings_title">Configurações avançadas</string>
<string name="keep_force_encryption">Keep force encryption</string>
<string name="keep_dm_verity">Keep dm-verity</string>
<string name="current_magisk_title">Versão instalada do Magisk: v%1$s</string>
<string name="install_magisk_title">Última versão do Magisk: v%1$.1f</string>
<!--Module Fragment-->
<string name="no_info_provided">(Nenhuma informação fornecida)</string>
<string name="no_modules_found">Nenhum módulo encontrado</string>
<string name="update_file_created">Módulo será atualizado na próxima reinicialização</string>
<string name="remove_file_created">Módulo será removido na próxima reinicialização</string>
<string name="remove_file_deleted">Módulo não será removido na próxima reinicialização</string>
<string name="disable_file_created">Módulo será desativado na próxima reinicialização</string>
<string name="disable_file_removed">Módulo será ativado na próxima reinicialização</string>
<string name="author">Criado por %1$s</string>
<string name="fab_flash_zip">Flashear Módulo Zip</string>
<!--Repo Fragment-->
<string name="update_available">Atualização disponível</string>
<string name="installed">Instalado</string>
<string name="not_installed">Não Instalado</string>
<string name="changelog">Registro de mudança</string>
<!--Log Fragment-->
<string name="menuSaveToSd">Salvar no SD</string>
<string name="menuSend">Enviar</string>
<string name="menuReload">Recarregar</string>
<string name="menuClearLog">Limpar registro agora</string>
<string name="logs_cleared">Registro limpado com sucesso</string>
<string name="log_is_empty">Registro está vazio</string>
<string name="logs_save_failed">Não foi possível gravar o registro para o cartão SD:</string>
<!--About Activity-->
<string name="about">Sobre</string>
<string name="app_developers">Principais desenvolvedores</string>
<string name="app_developers_"><![CDATA[App criado por <a href="https://github.com/topjohnwu">topjohnwu</a> em colaboração com <a href="https://github.com/d8ahazard">Digitalhigh</a> e <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Registro de Mudanças do App</string>
<string name="translators">Killer7Mod</string>
<string name="app_version">Versão do App</string>
<string name="app_source_code">Código fonte</string>
<string name="donation">Doação</string>
<string name="app_translators">Tradutores do App</string>
<string name="support_thread">Suporte</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">Este recurso não funcionará sem permissão de escrita do armazenamento externo.</string>
<string name="no_thanks">Não, Obrigado</string>
<string name="repo_install_title">Instalar %1$s</string>
<string name="repo_install_msg">Você deseja instalar%1$s ?</string>
<string name="download_install">Baixar &amp; instalar</string>
<string name="goto_install">Ir na seção \"Instalar\"</string>
<string name="download_file_error">Erro ao baixar o arquivo</string>
<string name="install_error">Erro na instalação!</string>
<string name="manual_install_1">Erro ao flashear o arquivo, arquivo zip colocado em %1$s</string>
<string name="manual_install_2">Flashear isto na recuperação manualmente</string>
<string name="invalid_zip">O zip não é um Módulo Magisk!!</string>
<string name="reboot_title">Instalação bem-sucedida!</string>
<string name="reboot_msg">Você quer reiniciar agora?</string>
<string name="reboot">Reiniciar</string>
<string name="copying_msg">Copiando zip para diretório temporário</string>
<string name="zip_install_progress_title">Instalando</string>
<string name="zip_install_unzip_zip_msg">Descompactando arquivo zip …</string>
<string name="zip_install_process_zip_msg">Processando arquivo zip …</string>
<string name="zip_install_progress_msg">"Instalando %1$s …"</string>
<string name="no_magisk_title">Magisk Não Instalado!</string>
<string name="no_magisk_msg">Você quer baixar e instalar o Magisk?</string>
<string name="downloading_toast">Baixando %1$s</string>
<string name="magisk_update_title">Nova atualização do Magisk disponível!</string>
<string name="magisk_update_message">Magisk v%1$.1f Atualização está pronta, você quer instalar?</string>
<string name="check_release_notes">Verificar as notas da atualização</string>
<!--Settings Fragment -->
<string name="settings_general_category">Geral</string>
<string name="settings_theme_title">Tema</string>
<string name="settings_theme_summary">Escolha um tema</string>
<string name="theme_default">Padrão</string>
<string name="theme_dark">Escuro</string>
<string name="settings_magiskhide_title">Ativar Magisk Hide</string>
<string name="settings_magiskhide_summary">Ocultar Magisk de várias detecções</string>
<string name="settings_busybox_title">Ativar BusyBox</string>
<string name="settings_busybox_summary">Monta a busybox interna do Magisk\'s para xbin</string>
<string name="settings_hosts_title">Ativar systemless hosts</string>
<string name="settings_hosts_summary">Suporte do systemless para Adblock apps</string>
<string name="settings_development_category">Desenvolvimento</string>
<string name="settings_developer_logging_title">Ativar registro mais detalhado</string>
<string name="settings_developer_logging_summary">Marque essa opção para habilitar um registro mais detalhado</string>
<string name="settings_shell_logging_title">Ativar registro da shell de comando</string>
<string name="settings_shell_logging_summary">Marque esta opção para habilitar o registro de todos os comandos shell e de saída</string>
<string name="settings_reboot_toast">Reinicie para aplicar configurações</string>
</resources>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>
</resources>

View File

@@ -1,127 +0,0 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
<string name="magiskhide">Magisk 隐藏</string>
<string name="modules">模块</string>
<string name="downloads">下载</string>
<string name="log">日志</string>
<string name="settings">设置</string>
<string name="status">状态</string>
<string name="install">安装</string>
<!--Status Fragment-->
<string name="magisk_version">已安装 Magisk v%1$s</string>
<string name="magisk_version_error">你是否已安装 Magisk</string>
<string name="checking_for_updates">正在检查更新…</string>
<string name="magisk_update_available">Magisk 可更新到 v%1$.1f</string>
<string name="cannot_check_updates">无法检查更新,没有网络连接?</string>
<string name="up_to_date">已安装最新版本的 %1$s</string>
<string name="root_error">已 ROOT 但没有 ROOT 权限,未授予权限?</string>
<string name="not_rooted">未 ROOT</string>
<string name="proper_root">已正确 ROOT</string>
<string name="checking_safetyNet_status">正在检查 SafetyNet 状态…</string>
<string name="safetyNet_error">无法检查 SafetyNet没有网络连接</string>
<string name="safetyNet_fail">SafetyNet 失败CTS 配置文件不匹配</string>
<string name="safetyNet_pass">SafetyNet 已通过</string>
<string name="root_info_warning">功能严重受限</string>
<!--Install Fragment-->
<string name="auto_detect">"(自动) %1$s"</string>
<string name="boot_image_title">Boot 镜像位置</string>
<string name="detect_button">检测</string>
<string name="advanced_settings_title">高级设置</string>
<string name="keep_force_encryption">保持强制加密</string>
<string name="keep_dm_verity">保持 dm-verity</string>
<string name="install_magisk_title">安装 Magisk 版本v%1$.1f</string>
<!--Module Fragment-->
<string name="no_info_provided">(未提供信息)</string>
<string name="no_modules_found">未找到模块</string>
<string name="update_file_created">模块将在下次重启后更新</string>
<string name="remove_file_created">模块将在下次重启后移除</string>
<string name="remove_file_deleted">模块将不会在下次重启后移除</string>
<string name="disable_file_created">模块将在下次重启后禁用</string>
<string name="disable_file_removed">模块将在下次重启后启用</string>
<string name="author">作者:%1$s</string>
<string name="fab_flash_zip">刷入模块 Zip</string>
<!--Repo Fragment-->
<string name="update_available">可更新</string>
<string name="installed">已安装</string>
<string name="not_installed">未安装</string>
<string name="changelog">更新日志</string>
<!--Log Fragment-->
<string name="menuSaveToSd">保存到 SD 卡</string>
<string name="menuSend">发送</string>
<string name="menuReload">重载</string>
<string name="menuClearLog">清除日志</string>
<string name="logs_cleared">日志已成功清除</string>
<string name="log_is_empty">日志为空</string>
<string name="logs_save_failed">无法将日志写入 SD 卡:</string>
<!--About Activity-->
<string name="about">关于</string>
<string name="app_developers">主要开发者</string>
<string name="app_developers_"><![CDATA[此应用由 <a href="https://github.com/topjohnwu">topjohnwu</a> 与 <a href="https://github.com/d8ahazard">Digitalhigh</a> 和 <a href="https://github.com/dvdandroid">Dvdandroid</a> 合作开发。]]></string>
<string name="app_changelog">应用更新日志</string>
<string name="translators">gh2923</string>
<string name="app_version">应用版本</string>
<string name="app_source_code">源代码</string>
<string name="donation">捐赠</string>
<string name="app_translators">翻译者</string>
<string name="support_thread">支持主题</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">未授予写入外置存储权限,此功能无法正常工作。</string>
<string name="no_thanks">不,谢谢</string>
<string name="repo_install_title">安装 %1$s</string>
<string name="repo_install_msg">你想要安装 %1$s 吗?</string>
<string name="download_install">下载并安装</string>
<string name="goto_install">前往“安装”界面</string>
<string name="download_file_error">下载文件时出错</string>
<string name="install_error">安装出错!</string>
<string name="manual_install_1">Zip 文件已保存至 %1$s</string>
<string name="manual_install_2">请在 Recovery 中手动刷入</string>
<string name="invalid_zip">此 zip 文件不是 Magisk 模块!!</string>
<string name="reboot_title">安装成功!</string>
<string name="reboot_msg">你想要立即重启吗?</string>
<string name="reboot">重启</string>
<string name="copying_msg">正在复制 zip 到临时目录</string>
<string name="zip_install_progress_title">正在安装</string>
<string name="zip_install_unzip_zip_msg">正在解压 zip 文件 …</string>
<string name="zip_install_process_zip_msg">正在处理 zip 文件 …</string>
<string name="zip_install_progress_msg">"正在安装 %1$s …"</string>
<string name="no_magisk_title">未安装 Magisk</string>
<string name="no_magisk_msg">你想要下载并安装 Magisk 吗?</string>
<string name="downloading_toast">正在下载 %1$s</string>
<!--URL Templates-->
<!--Settings Activity -->
<string name="settings_general_category">常规</string>
<string name="settings_theme_title">主题</string>
<string name="settings_theme_summary">选择一个主题</string>
<string name="theme_default">默认</string>
<string name="theme_dark">深色</string>
<string name="settings_magiskhide_title">启用 Magisk 隐藏</string>
<string name="settings_magiskhide_summary">隐藏 Magisk 使其不被多种方法检测到</string>
<string name="settings_busybox_title">启用 BusyBox</string>
<string name="settings_busybox_summary">将 Magisk 内置的 Busybox 挂载到 xbin</string>
<string name="settings_hosts_title">启用 Systemless hosts</string>
<string name="settings_hosts_summary">Systemless 支持广告屏蔽应用</string>
<string name="settings_development_category">开发</string>
<string name="settings_developer_logging_title">启用高级调试日志记录</string>
<string name="settings_developer_logging_summary">勾选此项以启用更加详细的日志记录。</string>
<string name="settings_shell_logging_title">启用 shell 命令调试日志记录</string>
<string name="settings_shell_logging_summary">勾选此项以启用对所有 shell 命令及输出的日志记录</string>
<string name="settings_reboot_toast">重启以应用设置</string>
</resources>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>@string/theme_default</item>
<item>@string/theme_dark</item>
</string-array>
<string-array name="themes_values">
<item>@string/theme_default_value</item>
<item>@string/theme_dark_value</item>
</string-array>
</resources>

View File

@@ -1,147 +0,0 @@
<resources>
<!--Universal-->
<string name="app_name" translatable="false">Magisk Manager</string>
<string name="magisk" translatable="false">Magisk</string>
<!--Welcome Activity-->
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
<string name="magiskhide">Magisk Hide</string>
<string name="modules">Modules</string>
<string name="downloads">Downloads</string>
<string name="log">Log</string>
<string name="settings">Settings</string>
<string name="status">Status</string>
<string name="install">Install</string>
<!--Status Fragment-->
<string name="magisk_version">Installed Magisk v%1$s</string>
<string name="magisk_version_error">Magisk not installed</string>
<string name="checking_for_updates">Checking for updates…</string>
<string name="magisk_update_available">Magisk v%1$.1f available!</string>
<string name="cannot_check_updates">Cannot check for updates, no Internet?</string>
<string name="up_to_date">Latest version of %1$s installed</string>
<string name="root_error">Rooted but no root permission, not allowed?</string>
<string name="not_rooted">Not rooted</string>
<string name="proper_root">Properly rooted</string>
<string name="safetyNet_check_text">Tap to start SafetyNet check</string>
<string name="checking_safetyNet_status">Checking SafetyNet status…</string>
<string name="safetyNet_connection_failed">Cannot connect to Google API</string>
<string name="safetyNet_connection_suspended">Connection to Google API was suspended</string>
<string name="safetyNet_error">Cannot check SafetyNet, no Internet?</string>
<string name="safetyNet_fail">SafetyNet Failed: CTS profile mismatch</string>
<string name="safetyNet_pass">SafetyNet Passed</string>
<string name="root_info_warning">Functionality greatly limited</string>
<!--Install Fragment-->
<string name="auto_detect">"(Auto) %1$s"</string>
<string name="boot_image_title">Boot Image Location</string>
<string name="detect_button">Detect</string>
<string name="advanced_settings_title">Advanced Settings</string>
<string name="keep_force_encryption">Keep force encryption</string>
<string name="keep_dm_verity">Keep dm-verity</string>
<string name="current_magisk_title">Installed Magisk Version: v%1$s</string>
<string name="install_magisk_title">Latest Magisk Version: v%1$.1f</string>
<string name="magiskify" translatable="false">Magiskify</string>
<!--Module Fragment-->
<string name="no_info_provided">(No info provided)</string>
<string name="no_modules_found">No modules found</string>
<string name="update_file_created">Module will be updated at next reboot</string>
<string name="remove_file_created">Module will be removed at next reboot</string>
<string name="remove_file_deleted">Module will not be removed at next reboot</string>
<string name="disable_file_created">Module will be disabled at next reboot</string>
<string name="disable_file_removed">Module will be enabled at next reboot</string>
<string name="author">Created by %1$s</string>
<string name="fab_flash_zip">Flash Module Zip</string>
<!--Repo Fragment-->
<string name="update_available">Update Available</string>
<string name="installed">Installed</string>
<string name="not_installed">Not Installed</string>
<string name="changelog">Changelog</string>
<!--Log Fragment-->
<string name="menuSaveToSd">Save to SD</string>
<string name="menuSend">Send</string>
<string name="menuReload">Reload</string>
<string name="menuClearLog">Clear log now</string>
<string name="logs_cleared">Log successfully cleared</string>
<string name="log_is_empty">Log is empty</string>
<string name="logs_save_failed">Could not write log to SD card:</string>
<!--About Activity-->
<string name="about">About</string>
<string name="app_developers">Main developers</string>
<string name="app_developers_"><![CDATA[App created by <a href="https://github.com/topjohnwu">topjohnwu</a> in collaboration with <a href="https://github.com/d8ahazard">Digitalhigh</a> and <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">App\'s changelog</string>
<string name="translators" />
<string name="app_version">App\'s version</string>
<string name="app_source_code">Source code</string>
<string name="donation">Donation</string>
<string name="app_translators">App\'s translators</string>
<string name="support_thread">Support thread</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">This feature will not work without permission to write external storage.</string>
<string name="no_thanks">No thanks</string>
<string name="repo_install_title">Install %1$s</string>
<string name="repo_install_msg">Do you want to install %1$s ?</string>
<string name="download_install">Download &amp; install</string>
<string name="goto_install">Go to \"Install\" section</string>
<string name="download_file_error">Error downloading file</string>
<string name="install_error">Installation error!</string>
<string name="manual_install_1">Zip file placed in %1$s</string>
<string name="manual_install_2">Flash it in recovery manually</string>
<string name="invalid_zip">The zip is not a Magisk Module!!</string>
<string name="reboot_title">Installation succeeded!</string>
<string name="reboot_msg">Do you want to reboot now?</string>
<string name="reboot">Reboot</string>
<string name="copying_msg">Copying zip to temp directory</string>
<string name="zip_install_progress_title">Installing</string>
<string name="zip_install_unzip_zip_msg">Unzipping zip file …</string>
<string name="zip_install_process_zip_msg">Processing zip file …</string>
<string name="zip_install_progress_msg">"Installing %1$s …"</string>
<string name="no_magisk_title">No Magisk Installed!</string>
<string name="no_magisk_msg">Do you want to download and install Magisk?</string>
<string name="downloading_toast">Downloading %1$s</string>
<string name="magisk_update_title">New Magisk Update Available!</string>
<string name="magisk_update_message">Magisk v%1$.1f update is live, do you want to install?</string>
<string name="settings_reboot_toast">Reboot to apply settings</string>
<string name="check_release_notes">Check release notes</string>
<string name="repo_cache_cleared">Repo cache cleared</string>
<!--URL Templates-->
<string name="url_main" translatable="false">https://api.github.com/orgs/Magisk-Modules-Repo/repos</string>
<string name="file_url" translatable="false">https://raw.githubusercontent.com/Magisk-Modules-Repo/%1$s/master/%2$s</string>
<string name="zip_url" translatable="false">https://github.com/Magisk-Modules-Repo/%1$s/archive/master.zip</string>
<!--Settings Activity -->
<string name="settings_general_category">General</string>
<string name="settings_theme_title">Theme</string>
<string name="settings_theme_summary">Select a theme</string>
<string name="theme_default">Default</string>
<string name="theme_dark">Dark</string>
<string name="settings_clear_cache_title">Clear Repo Cache</string>
<string name="settings_clear_cache_summary">Clear the cached information for online repos, forces the app to refresh online</string>
<string name="settings_magiskhide_title">Enable Magisk Hide</string>
<string name="settings_magiskhide_summary">Hide Magisk from various detections</string>
<string name="settings_busybox_title">Enable BusyBox</string>
<string name="settings_busybox_summary">Bind mount Magisk\'s built-in busybox to xbin</string>
<string name="settings_hosts_title">Enable systemless hosts</string>
<string name="settings_hosts_summary">Systemless support for Adblock apps</string>
<string name="settings_development_category">Development</string>
<string name="settings_developer_logging_title">Enable advanced debug logging</string>
<string name="settings_developer_logging_summary">Check this to enable verbose logging</string>
<string name="settings_shell_logging_title">Enable shell command debug logging</string>
<string name="settings_shell_logging_summary">Check this to enable logging all shell commands and its output</string>
<!-- Themes -->
<string name="theme_default_value" translatable="false">default</string>
<string name="theme_dark_value" translatable="false">dark</string>
</resources>

View File

@@ -1,124 +0,0 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
<item name="windowActionModeOverlay">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="cardStyle">@style/CardViewStyle.Light</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
<style name="AppTheme.dh" parent="ThemeOverlay.AppCompat.Dark">
<item name="colorPrimary">@color/dh_primary</item>
<item name="colorPrimaryDark">@color/dh_primary_dark</item>
<item name="colorAccent">@color/dh_accent</item>
<item name="windowActionModeOverlay">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="cardBackgroundColor">@color/dh_accent</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="cardStyle">@style/CardViewStyle.Dark</item>
<item name="android:textColorPrimary">@color/dh_primary_text</item>
<item name="android:textColorSecondary">@color/dh_primary_text</item>
<item name="android:alertDialogTheme">@style/AlertDialog.dh</item>
<item name="android:dialogTheme">@style/AlertDialog.dh</item>
<item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
<item name="colorControlNormal">@color/dh_primary_text</item>
<item name="colorControlActivated">@color/dh_accent</item>
<item name="colorControlHighlight">@color/dh_icons</item>
</style>
<style name="OverFlow" parent="@android:style/Widget.ActionBar">
<item name="android:backgroundTint">@color/dh_icons</item>
</style>
<style name="AppTheme.Toolbar.dh" parent="ThemeOverlay.AppCompat.Dark">
<item name="colorPrimary">@color/dh_primary</item>
<item name="android:elevation">4dp</item>
<item name="android:textColorPrimary">@color/dh_primary_text</item>
<item name="android:textColorSecondary">@color/dh_primary_text</item> <!-- force -->
<item name="actionMenuTextColor">@color/dh_icons</item>
</style>
<style name="CardViewStyle.Dark" parent="CardView">
<item name="cardBackgroundColor">@android:color/background_dark</item>
</style>
<style name="AlertDialog.dh" parent="ThemeOverlay.AppCompat.Dark">
<item name="android:textColor">@color/dh_primary_text</item>
<item name="android:textColorSecondary">@color/dh_primary_text</item>
<item name="colorAccent">@color/dh_accent</item>
<item name="android:textColorPrimary">@color/dh_primary_text</item>
<item name="android:background">@color/dh_divider</item>
<item name="android:textColorAlertDialogListItem">@color/dh_primary_text</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowTitleStyle">@style/MyTitleTextStyle</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="android:alertDialogStyle">@style/ListPrefAlertDialogStyle</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:textAppearanceMedium">@style/MyAlertTextAppearance</item>
</style>
<style name="MyAlertTextAppearance">
<!-- Set text size and color of title and message here -->
<item name="android:textColor">@color/dh_primary_text</item>
</style>
<style name="ListPrefAlertDialogStyle">
<item name="android:fullDark" >@android:color/transparent</item>
<item name="android:topDark" >@android:color/transparent</item>
<item name="android:centerDark" >@android:color/transparent</item>
<item name="android:bottomDark" >@android:color/transparent</item>
<item name="android:fullBright" >@android:color/transparent</item>
<item name="android:topBright" >@android:color/transparent</item>
<item name="android:centerBright">@android:color/transparent</item>
<item name="android:bottomBright">@android:color/transparent</item>
<item name="android:bottomMedium">@android:color/transparent</item>
<item name="android:centerMedium">@android:color/transparent</item>
</style>
<style name="ListViewStyle.dh" parent="android:Widget.Material.PopupMenu">
<item name="android:divider">@color/dh_divider</item>
<item name="android:dividerHeight">1dp</item>
<item name="android:listSelector">@android:color/background_dark</item>
<item name="android:drawSelectorOnTop">true</item>
<item name="android:cacheColorHint">@color/dh_divider</item>
<item name="android:foregroundTint">@color/dh_primary_text</item>
</style>
<style name="TransparentListView" parent="@android:style/Widget.ListView">
<item name="android:cacheColorHint">@android:color/transparent</item>
<item name="android:background">#FFFF00</item>
<item name="android:listSelector">@android:color/transparent</item>
<item name="android:divider">#F78181</item>
<item name="android:dividerHeight">3dp</item>
</style>
<style name="MyTitleTextStyle">
<item name="android:textColor">@color/dh_primary_text</item>
<item name="android:textAppearance">@style/TextAppearance.AppCompat.Title</item>
</style>
<style name="CardViewStyle.Light" parent="CardView">
<item name="cardBackgroundColor">@android:color/background_light</item>
</style>
<style name="AppTheme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
</style>
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@drawable/ic_splash_activity</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
</resources>

View File

@@ -1,62 +0,0 @@
<PreferenceScreen
android:layout_marginTop="?attr/actionBarSize"
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/settings_general_category">
<ListPreference
android:key="theme"
android:title="@string/settings_theme_title"
android:summary="@string/settings_theme_summary"
android:defaultValue="@string/theme_default_value"
android:entries="@array/themes"
android:entryValues="@array/themes_values"/>
<Preference
android:key="clear"
android:title="@string/settings_clear_cache_title"
android:summary="@string/settings_clear_cache_summary" />
</PreferenceCategory>
<PreferenceCategory
android:title="Magisk">
<CheckBoxPreference
android:key="magiskhide"
android:defaultValue="false"
android:title="@string/settings_magiskhide_title"
android:summary="@string/settings_magiskhide_summary" />
<CheckBoxPreference
android:key="busybox"
android:defaultValue="false"
android:title="@string/settings_busybox_title"
android:summary="@string/settings_busybox_summary" />
<CheckBoxPreference
android:key="hosts"
android:defaultValue="false"
android:title="@string/settings_hosts_title"
android:summary="@string/settings_hosts_summary" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/settings_development_category">
<CheckBoxPreference
android:key="developer_logging"
android:defaultValue="false"
android:title="@string/settings_developer_logging_title"
android:summary="@string/settings_developer_logging_summary" />
<CheckBoxPreference
android:key="shell_logging"
android:defaultValue="false"
android:title="@string/settings_shell_logging_title"
android:summary="@string/settings_shell_logging_summary" />
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -1,25 +1,65 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply plugin: 'com.android.application'
buildscript {
repositories {
jcenter()
mavenCentral()
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.topjohnwu.magisk"
minSdkVersion 21
targetSdkVersion rootProject.ext.compileSdkVersion
javaCompileOptions {
annotationProcessorOptions {
argument('butterknife.debuggable', 'false')
}
}
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
flavorDimensions "mode"
productFlavors {
full {
versionCode 125
versionName "5.8.0"
}
stub {
versionCode 1
versionName "stub"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
dexOptions {
preDexLibraries true
javaMaxHeapSize "2g"
}
lintOptions {
disable 'MissingTranslation'
}
}
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
fullImplementation project(':utils')
implementation "com.android.support:support-core-utils:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:preference-v7:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:design:${rootProject.ext.supportLibVersion}"
fullImplementation 'com.github.topjohnwu:libsu:1.2.0'
fullImplementation 'com.atlassian.commonmark:commonmark:0.11.0'
fullImplementation 'org.kamranzafar:jtar:2.3'
fullImplementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}

View File

@@ -1,18 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
org.gradle.parallel=true

Binary file not shown.

View File

@@ -1,6 +0,0 @@
#Wed Aug 17 11:39:12 CEST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

160
gradlew vendored
View File

@@ -1,160 +0,0 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
gradlew.bat vendored
View File

@@ -1,90 +0,0 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

29
proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,29 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/topjohnwu/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Keep all names, we are open source anyway :)
-keepnames class ** { *; }
# BouncyCastle
-keep class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
-keep class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
-keep class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
-dontwarn javax.naming.**
# Gson
-keepattributes Signature

View File

@@ -1 +0,0 @@
include ':app'

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.topjohnwu.magisk">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application
android:name=".MagiskManager"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:exported="true" />
<activity
android:name=".SplashActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".AboutActivity"
android:theme="@style/AppTheme.StatusBar" />
<activity
android:name=".SettingsActivity"
android:theme="@style/AppTheme.StatusBar" />
<activity
android:name=".FlashActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="nosensor"
android:theme="@style/AppTheme.StatusBar" />
<activity
android:name=".NoUIActivity"
android:theme="@style/AppTheme.Translucent" />
<activity
android:name=".superuser.RequestActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity="internal.superuser"
android:theme="@style/SuRequest" />
<receiver android:name=".superuser.SuReceiver" />
<receiver android:name=".receivers.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".receivers.PackageReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver android:name=".receivers.ManagerUpdate" />
<receiver android:name=".receivers.RebootReceiver" />
<receiver android:name=".receivers.ShortcutReceiver">
<intent-filter>
<action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
</receiver>
<service android:name=".services.OnBootIntentService" />
<service
android:name=".services.UpdateCheckService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<!-- Hardcode GMS version -->
<meta-data
android:name="com.google.android.gms.version"
android:value="7095000" />
</application>
</manifest>

View File

@@ -0,0 +1,80 @@
package com.topjohnwu.magisk;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.View;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.utils.Const;
import java.util.Locale;
import butterknife.BindView;
import butterknife.ButterKnife;
public class AboutActivity extends Activity {
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.app_version_info) AboutCardRow appVersionInfo;
@BindView(R.id.app_changelog) AboutCardRow appChangelog;
@BindView(R.id.app_translators) AboutCardRow appTranslators;
@BindView(R.id.app_source_code) AboutCardRow appSourceCode;
@BindView(R.id.support_thread) AboutCardRow supportThread;
@BindView(R.id.donation) AboutCardRow donation;
@Override
public int getDarkTheme() {
return R.style.AppTheme_StatusBar_Dark;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(R.string.about);
ab.setDisplayHomeAsUpEnabled(true);
}
appVersionInfo.setSummary(String.format(Locale.US, "%s (%d) (%s)",
BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, getPackageName()));
appChangelog.removeSummary();
appChangelog.setOnClickListener(v -> {
new MarkDownWindow(this, getString(R.string.app_changelog),
getResources().openRawResource(R.raw.changelog)).exec();
});
String translators = getString(R.string.translators);
if (TextUtils.isEmpty(translators)) {
appTranslators.setVisibility(View.GONE);
} else {
appTranslators.setSummary(translators);
}
appSourceCode.removeSummary();
appSourceCode.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Const.Url.SOURCE_CODE_URL))));
supportThread.removeSummary();
supportThread.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Const.Url.XDA_THREAD))));
donation.removeSummary();
donation.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Const.Url.DONATION_URL))));
setFloating();
}
}

View File

@@ -0,0 +1,155 @@
package com.topjohnwu.magisk;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.FlashZip;
import com.topjohnwu.magisk.asyncs.InstallMagisk;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.superuser.CallbackList;
import com.topjohnwu.superuser.Shell;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class FlashActivity extends Activity {
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.txtLog) TextView flashLogs;
@BindView(R.id.button_panel) public LinearLayout buttonPanel;
@BindView(R.id.reboot) public Button reboot;
@BindView(R.id.scrollView) ScrollView sv;
private List<String> logs;
@OnClick(R.id.no_thanks)
void dismiss() {
finish();
}
@OnClick(R.id.reboot)
void reboot() {
Shell.Async.su("/system/bin/reboot");
}
@OnClick(R.id.save_logs)
void saveLogs() {
Calendar now = Calendar.getInstance();
String filename = String.format(Locale.US,
"install_log_%04d%02d%02d_%02d%02d%02d.log",
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
File logFile = new File(Const.EXTERNAL_PATH + "/logs", filename);
logFile.getParentFile().mkdirs();
try (FileWriter writer = new FileWriter(logFile)) {
for (String s : logs) {
writer.write(s);
writer.write('\n');
}
} catch (IOException e) {
e.printStackTrace();
return;
}
MagiskManager.toast(logFile.getPath(), Toast.LENGTH_LONG);
}
@Override
public int getDarkTheme() {
return R.style.AppTheme_StatusBar_Dark;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flash);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(R.string.flashing);
}
setFloating();
setFinishOnTouchOutside(false);
if (!Shell.rootAccess())
reboot.setVisibility(View.GONE);
logs = new ArrayList<>();
CallbackList<String> console = new CallbackList<String>(new ArrayList<>()) {
@Override
public void onAddElement(String s) {
logs.add(s);
flashLogs.setText(TextUtils.join("\n", this));
sv.postDelayed(() -> sv.fullScroll(ScrollView.FOCUS_DOWN), 10);
}
};
// We must receive a Uri of the target zip
Intent intent = getIntent();
Uri uri = intent.getData();
switch (intent.getStringExtra(Const.Key.FLASH_ACTION)) {
case Const.Value.FLASH_ZIP:
new FlashZip(this, uri, console, logs).exec();
break;
case Const.Value.UNINSTALL:
new UninstallMagisk(this, uri, console, logs).exec();
break;
case Const.Value.FLASH_MAGISK:
new InstallMagisk(this, console, logs, uri, InstallMagisk.DIRECT_MODE).exec();
break;
case Const.Value.FLASH_SECOND_SLOT:
new InstallMagisk(this, console, logs, uri, InstallMagisk.SECOND_SLOT_MODE).exec();
break;
case Const.Value.PATCH_BOOT:
new InstallMagisk(this, console, logs, uri,
intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT)).exec();
break;
}
}
@Override
public void onBackPressed() {
// Prevent user accidentally press back button
}
private static class UninstallMagisk extends FlashZip {
private UninstallMagisk(Activity context, Uri uri, List<String> console, List<String> logs) {
super(context, uri, console, logs);
}
@Override
protected void onPostExecute(Integer result) {
if (result == 1) {
new Handler().postDelayed(() ->
RootUtils.uninstallPkg(getActivity().getPackageName()), 3000);
} else {
super.onPostExecute(result);
}
}
}
}

View File

@@ -0,0 +1,55 @@
package com.topjohnwu.magisk;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.Const;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class LogFragment extends Fragment {
private Unbinder unbinder;
@BindView(R.id.container) ViewPager viewPager;
@BindView(R.id.tab) TabLayout tab;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_log, container, false);
unbinder = ButterKnife.bind(this, v);
((MainActivity) getActivity()).toolbar.setElevation(0);
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
if (!(Const.USER_ID > 0 && getApplication().multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
}
adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
tab.setupWithViewPager(viewPager);
tab.setVisibility(View.VISIBLE);
viewPager.setAdapter(adapter);
return v;
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -0,0 +1,313 @@
package com.topjohnwu.magisk;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.CardView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.topjohnwu.magisk.asyncs.CheckSafetyNet;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.ShowUI;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
public class MagiskFragment extends Fragment
implements Topic.Subscriber, SwipeRefreshLayout.OnRefreshListener, ExpandableView {
private Container expandableContainer = new Container();
private MagiskManager mm;
private Unbinder unbinder;
private static boolean shownDialog = false;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.magisk_update) RelativeLayout magiskUpdate;
@BindView(R.id.magisk_update_icon) ImageView magiskUpdateIcon;
@BindView(R.id.magisk_update_status) TextView magiskUpdateText;
@BindView(R.id.magisk_update_progress) ProgressBar magiskUpdateProgress;
@BindView(R.id.magisk_status_icon) ImageView magiskStatusIcon;
@BindView(R.id.magisk_version) TextView magiskVersionText;
@BindView(R.id.safetyNet_card) CardView safetyNetCard;
@BindView(R.id.safetyNet_refresh) ImageView safetyNetRefreshIcon;
@BindView(R.id.safetyNet_status) TextView safetyNetStatusText;
@BindView(R.id.safetyNet_check_progress) ProgressBar safetyNetProgress;
@BindView(R.id.expand_layout) LinearLayout expandLayout;
@BindView(R.id.cts_status_icon) ImageView ctsStatusIcon;
@BindView(R.id.cts_status) TextView ctsStatusText;
@BindView(R.id.basic_status_icon) ImageView basicStatusIcon;
@BindView(R.id.basic_status) TextView basicStatusText;
@BindView(R.id.install_option_card) CardView installOptionCard;
@BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
@BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
@BindView(R.id.install_button) CardView installButton;
@BindView(R.id.install_text) TextView installText;
@BindView(R.id.uninstall_button) CardView uninstallButton;
@BindColor(R.color.red500) int colorBad;
@BindColor(R.color.green500) int colorOK;
@BindColor(R.color.yellow500) int colorWarn;
@BindColor(R.color.grey500) int colorNeutral;
@BindColor(R.color.blue500) int colorInfo;
@OnClick(R.id.safetyNet_title)
void safetyNet() {
Runnable task = () -> {
safetyNetProgress.setVisibility(View.VISIBLE);
safetyNetRefreshIcon.setVisibility(View.GONE);
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
new CheckSafetyNet(getActivity()).exec();
collapse();
};
if (!TextUtils.equals(mm.getPackageName(), Const.ORIG_PKG_NAME)) {
new AlertDialogBuilder(getActivity())
.setTitle(R.string.cannot_check_sn_title)
.setMessage(R.string.cannot_check_sn_notice)
.setCancelable(true)
.setPositiveButton(R.string.ok, null)
.show();
} else if (!CheckSafetyNet.dexPath.exists()) {
// Show dialog
new AlertDialogBuilder(getActivity())
.setTitle(R.string.proprietary_title)
.setMessage(R.string.proprietary_notice)
.setCancelable(true)
.setPositiveButton(R.string.yes, (d, i) -> task.run())
.setNegativeButton(R.string.no_thanks, null)
.show();
} else {
task.run();
}
}
@OnClick(R.id.install_button)
void install() {
shownDialog = true;
// Show Manager update first
if (mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
ShowUI.managerInstallDialog(getActivity());
return;
}
((NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
ShowUI.magiskInstallDialog(getActivity());
}
@OnClick(R.id.uninstall_button)
void uninstall() {
ShowUI.uninstallDialog(getActivity());
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_magisk, container, false);
unbinder = ButterKnife.bind(this, v);
getActivity().setTitle(R.string.magisk);
mm = getApplication();
expandableContainer.expandLayout = expandLayout;
setupExpandable();
keepVerityChkbox.setChecked(mm.keepVerity);
keepVerityChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepVerity = checked);
keepEncChkbox.setChecked(mm.keepEnc);
keepEncChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepEnc = checked);
mSwipeRefreshLayout.setOnRefreshListener(this);
updateUI();
return v;
}
@Override
public void onRefresh() {
mm.loadMagiskInfo();
updateUI();
magiskUpdateText.setText(R.string.checking_for_updates);
magiskUpdateProgress.setVisibility(View.VISIBLE);
magiskUpdateIcon.setVisibility(View.GONE);
safetyNetStatusText.setText(R.string.safetyNet_check_text);
mm.safetyNetDone.reset();
mm.updateCheckDone.reset();
mm.remoteMagiskVersionString = null;
mm.remoteMagiskVersionCode = -1;
collapse();
shownDialog = false;
// Trigger state check
if (Utils.checkNetworkStatus()) {
new CheckUpdates().exec();
} else {
mSwipeRefreshLayout.setRefreshing(false);
}
}
@Override
public void onTopicPublished(Topic topic) {
if (topic == mm.updateCheckDone) {
updateCheckUI();
} else if (topic == mm.safetyNetDone) {
updateSafetyNetUI((int) topic.getResults()[0]);
}
}
@Override
public Topic[] getSubscription() {
return new Topic[] { mm.updateCheckDone, mm.safetyNetDone };
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public Container getContainer() {
return expandableContainer;
}
private void updateUI() {
((MainActivity) getActivity()).checkHideSection();
boolean hasNetwork = Utils.checkNetworkStatus();
boolean hasRoot = Shell.rootAccess();
boolean isUpToDate = mm.magiskVersionCode > Const.MAGISK_VER.UNIFIED;
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
safetyNetCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
installOptionCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
uninstallButton.setVisibility(isUpToDate && hasRoot ? View.VISIBLE : View.GONE);
int image, color;
if (mm.magiskVersionCode < 0) {
color = colorBad;
image = R.drawable.ic_cancel;
magiskVersionText.setText(R.string.magisk_version_error);
} else {
color = colorOK;
image = R.drawable.ic_check_circle;
magiskVersionText.setText(getString(R.string.current_magisk_title, "v" + mm.magiskVersionString));
}
magiskStatusIcon.setImageResource(image);
magiskStatusIcon.setColorFilter(color);
}
private void updateCheckUI() {
int image, color;
if (mm.remoteMagiskVersionCode < 0) {
color = colorNeutral;
image = R.drawable.ic_help;
magiskUpdateText.setText(R.string.invalid_update_channel);
installButton.setVisibility(View.GONE);
} else {
color = colorOK;
image = R.drawable.ic_check_circle;
magiskUpdateText.setText(getString(R.string.install_magisk_title, "v" + mm.remoteMagiskVersionString));
installButton.setVisibility(View.VISIBLE);
if (mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
installText.setText(getString(R.string.update, getString(R.string.app_name)));
} else if (mm.magiskVersionCode > 0 && mm.remoteMagiskVersionCode > mm.magiskVersionCode) {
installText.setText(getString(R.string.update, getString(R.string.magisk)));
} else {
installText.setText(R.string.install);
}
}
magiskUpdateIcon.setImageResource(image);
magiskUpdateIcon.setColorFilter(color);
magiskUpdateIcon.setVisibility(View.VISIBLE);
magiskUpdateProgress.setVisibility(View.GONE);
mSwipeRefreshLayout.setRefreshing(false);
if (!shownDialog) {
if (mm.remoteMagiskVersionCode > mm.magiskVersionCode
|| mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
install();
} else if (mm.remoteMagiskVersionCode >= Const.MAGISK_VER.FIX_ENV &&
!ShellUtils.fastCmdResult("env_check")) {
ShowUI.envFixDialog(getActivity());
}
}
}
private void updateSafetyNetUI(int response) {
safetyNetProgress.setVisibility(View.GONE);
safetyNetRefreshIcon.setVisibility(View.VISIBLE);
if ((response & 0x0F) == 0) {
safetyNetStatusText.setText(R.string.safetyNet_check_success);
boolean b;
b = (response & ISafetyNetHelper.CTS_PASS) != 0;
ctsStatusText.setText("ctsProfile: " + b);
ctsStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
ctsStatusIcon.setColorFilter(b ? colorOK : colorBad);
b = (response & ISafetyNetHelper.BASIC_PASS) != 0;
basicStatusText.setText("basicIntegrity: " + b);
basicStatusIcon.setImageResource(b ? R.drawable.ic_check_circle : R.drawable.ic_cancel);
basicStatusIcon.setColorFilter(b ? colorOK : colorBad);
expand();
} else {
@StringRes int resid;
switch (response) {
case ISafetyNetHelper.CAUSE_SERVICE_DISCONNECTED:
resid = R.string.safetyNet_network_loss;
break;
case ISafetyNetHelper.CAUSE_NETWORK_LOST:
resid = R.string.safetyNet_service_disconnected;
break;
case ISafetyNetHelper.RESPONSE_ERR:
resid = R.string.safetyNet_res_invalid;
break;
case ISafetyNetHelper.CONNECTION_FAIL:
default:
resid = R.string.safetyNet_api_error;
break;
}
safetyNetStatusText.setText(resid);
}
}
}

View File

@@ -0,0 +1,92 @@
package com.topjohnwu.magisk;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SearchView;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.Topic;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
private ApplicationAdapter appAdapter;
private SearchView.OnQueryTextListener searchListener;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
unbinder = ButterKnife.bind(this, view);
mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> appAdapter.refresh());
appAdapter = new ApplicationAdapter();
recyclerView.setAdapter(appAdapter);
searchListener = new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
appAdapter.filter(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
appAdapter.filter(newText);
return false;
}
};
getActivity().setTitle(R.string.magiskhide);
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_magiskhide, menu);
SearchView search = (SearchView) menu.findItem(R.id.app_search).getActionView();
search.setOnQueryTextListener(searchListener);
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public void onTopicPublished(Topic topic) {
mSwipeRefreshLayout.setRefreshing(false);
appAdapter.filter(null);
}
@Override
public Topic[] getSubscription() {
return new Topic[] { getApplication().magiskHideDone };
}
}

View File

@@ -0,0 +1,145 @@
package com.topjohnwu.magisk;
import android.Manifest;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class MagiskLogFragment extends Fragment {
private Unbinder unbinder;
@BindView(R.id.txtLog) TextView txtLog;
@BindView(R.id.svLog) ScrollView svLog;
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
@BindView(R.id.progressBar) ProgressBar progressBar;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_magisk_log, container, false);
unbinder = ButterKnife.bind(this, view);
setHasOptionsMenu(true);
txtLog.setTextIsSelectable(true);
return view;
}
@Override
public void onStart() {
super.onStart();
getActivity().setTitle(R.string.log);
}
@Override
public void onResume() {
super.onResume();
readLogs();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_refresh:
readLogs();
return true;
case R.id.menu_save:
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, this::saveLogs);
return true;
case R.id.menu_clear:
clearLogs();
return true;
default:
return true;
}
}
public void readLogs() {
Shell.Async.su(new Shell.Async.Callback() {
@Override
public void onTaskResult(@Nullable List<String> out, @Nullable List<String> err) {
progressBar.setVisibility(View.GONE);
if (ShellUtils.isValidOutput(out)) {
txtLog.setText(TextUtils.join("\n", out));
} else {
txtLog.setText(R.string.log_is_empty);
}
svLog.postDelayed(() -> svLog.fullScroll(ScrollView.FOCUS_DOWN), 100);
hsvLog.postDelayed(() -> hsvLog.fullScroll(ScrollView.FOCUS_LEFT), 100);
}
@Override
public void onTaskError(@NonNull Throwable throwable) {
txtLog.setText(R.string.log_is_empty);
}
}, "cat " + Const.MAGISK_LOG + " | tail -n 5000");
}
public void saveLogs() {
Calendar now = Calendar.getInstance();
String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log",
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
File targetFile = new File(Const.EXTERNAL_PATH + "/logs", filename);
targetFile.getParentFile().mkdirs();
try {
targetFile.createNewFile();
} catch (IOException e) {
return;
}
Shell.Async.su(new Shell.Async.Callback() {
@Override
public void onTaskResult(@Nullable List<String> out, @Nullable List<String> err) {
SnackbarMaker.make(txtLog, targetFile.getPath(), Snackbar.LENGTH_SHORT).show();
}
@Override
public void onTaskError(@NonNull Throwable throwable) {}
}, "cat " + Const.MAGISK_LOG + " > " + targetFile);
}
public void clearLogs() {
Shell.Async.su("echo -n > " + Const.MAGISK_LOG);
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,292 @@
package com.topjohnwu.magisk;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Xml;
import com.topjohnwu.magisk.components.Application;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.ShellInitializer;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class MagiskManager extends Application implements Shell.Container {
// Topics
public final Topic magiskHideDone = new Topic();
public final Topic reloadActivity = new Topic();
public final Topic moduleLoadDone = new Topic();
public final Topic repoLoadDone = new Topic();
public final Topic updateCheckDone = new Topic();
public final Topic safetyNetDone = new Topic();
public final Topic localeDone = new Topic();
// Info
public boolean hasInit = false;
public String magiskVersionString;
public int magiskVersionCode = -1;
public String remoteMagiskVersionString;
public int remoteMagiskVersionCode = -1;
public String remoteManagerVersionString;
public int remoteManagerVersionCode = -1;
public String magiskLink;
public String magiskNoteLink;
public String managerLink;
public String managerNoteLink;
public String uninstallerLink;
public boolean keepVerity = false;
public boolean keepEnc = false;
// Data
public Map<String, Module> moduleMap;
public List<Locale> locales;
public boolean magiskHide;
public boolean isDarkTheme;
public int suRequestTimeout;
public int suLogTimeout = 14;
public int suAccessState;
public int multiuserMode;
public int suResponseType;
public int suNotificationType;
public int suNamespaceMode;
public String localeConfig;
public int updateChannel;
public String bootFormat;
public int repoOrder;
// Global resources
public SharedPreferences prefs;
public MagiskDatabaseHelper mDB;
public RepoDatabaseHelper repoDB;
private volatile Shell mShell;
public MagiskManager() {
weakSelf = new WeakReference<>(this);
Shell.setContainer(this);
}
@Nullable
@Override
public Shell getShell() {
return mShell;
}
@Override
public void setShell(@Nullable Shell shell) {
mShell = shell;
}
@Override
public void onCreate() {
super.onCreate();
Shell.setFlags(Shell.FLAG_MOUNT_MASTER);
Shell.verboseLogging(BuildConfig.DEBUG);
Shell.setInitializer(ShellInitializer.class);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
mDB = MagiskDatabaseHelper.getInstance(this);
String pkg = mDB.getStrings(Const.Key.SU_MANAGER, null);
if (pkg != null && getPackageName().equals(Const.ORIG_PKG_NAME)) {
mDB.setStrings(Const.Key.SU_MANAGER, null);
RootUtils.uninstallPkg(pkg);
}
if (TextUtils.equals(pkg, getPackageName())) {
try {
// We are the manager, remove com.topjohnwu.magisk as it could be malware
getPackageManager().getApplicationInfo(Const.ORIG_PKG_NAME, 0);
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
} catch (PackageManager.NameNotFoundException ignored) {}
}
setLocale();
loadConfig();
}
public static MagiskManager get() {
return (MagiskManager) weakSelf.get();
}
public void setLocale() {
localeConfig = prefs.getString(Const.Key.LOCALE, "");
if (localeConfig.isEmpty()) {
locale = defaultLocale;
} else {
locale = Locale.forLanguageTag(localeConfig);
}
Resources res = getBaseContext().getResources();
Configuration config = new Configuration(res.getConfiguration());
config.setLocale(locale);
res.updateConfiguration(config, res.getDisplayMetrics());
}
public void loadConfig() {
// su
suRequestTimeout = Utils.getPrefsInt(prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
suResponseType = Utils.getPrefsInt(prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
suNotificationType = Utils.getPrefsInt(prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
suAccessState = mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
multiuserMode = mDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY);
suNamespaceMode = mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
// config
isDarkTheme = prefs.getBoolean(Const.Key.DARK_THEME, false);
updateChannel = Utils.getPrefsInt(prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
bootFormat = prefs.getString(Const.Key.BOOT_FORMAT, ".img");
repoOrder = prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_NAME);
}
public void writeConfig() {
prefs.edit()
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
.putBoolean(Const.Key.HOSTS, Const.MAGISK_HOST_FILE.exists())
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(suNotificationType))
.putString(Const.Key.ROOT_ACCESS, String.valueOf(suAccessState))
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(multiuserMode))
.putString(Const.Key.SU_MNT_NS, String.valueOf(suNamespaceMode))
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(updateChannel))
.putString(Const.Key.LOCALE, localeConfig)
.putString(Const.Key.BOOT_FORMAT, bootFormat)
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
.putInt(Const.Key.REPO_ORDER, repoOrder)
.apply();
}
public void loadMagiskInfo() {
try {
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
String s = ShellUtils.fastCmd((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ?
"resetprop -p " : "getprop ") + Const.MAGISKHIDE_PROP);
magiskHide = s == null || Integer.parseInt(s) != 0;
} catch (Exception ignored) {}
}
public void getDefaultInstallFlags() {
keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
}
public void setupUpdateCheck() {
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
if (prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
if (scheduler.getAllPendingJobs().isEmpty() ||
Const.UPDATE_SERVICE_VER > prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
ComponentName service = new ComponentName(this, UpdateCheckService.class);
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(8 * 60 * 60 * 1000)
.build();
scheduler.schedule(info);
}
} else {
scheduler.cancel(Const.UPDATE_SERVICE_VER);
}
}
public void dumpPrefs() {
// Flush prefs to disk
prefs.edit().commit();
File xml = new File(getFilesDir().getParent() + "/shared_prefs",
getPackageName() + "_preferences.xml");
Shell.Sync.su(Utils.fmt("for usr in /data/user/*; do cat %s > ${usr}/%s; done", xml, Const.MANAGER_CONFIGS));
}
public void loadPrefs() {
SuFile config = new SuFile(Utils.fmt("/data/user/%d/%s", Const.USER_ID, Const.MANAGER_CONFIGS));
if (config.exists()) {
SharedPreferences.Editor editor = prefs.edit();
try {
SuFileInputStream is = new SuFileInputStream(config);
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(is, "UTF-8");
parser.nextTag();
parser.require(XmlPullParser.START_TAG, null, "map");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG)
continue;
String key = parser.getAttributeValue(null, "name");
String value = parser.getAttributeValue(null, "value");
switch (parser.getName()) {
case "string":
parser.require(XmlPullParser.START_TAG, null, "string");
editor.putString(key, parser.nextText());
parser.require(XmlPullParser.END_TAG, null, "string");
break;
case "boolean":
parser.require(XmlPullParser.START_TAG, null, "boolean");
editor.putBoolean(key, Boolean.parseBoolean(value));
parser.nextTag();
parser.require(XmlPullParser.END_TAG, null, "boolean");
break;
case "int":
parser.require(XmlPullParser.START_TAG, null, "int");
editor.putInt(key, Integer.parseInt(value));
parser.nextTag();
parser.require(XmlPullParser.END_TAG, null, "int");
break;
case "long":
parser.require(XmlPullParser.START_TAG, null, "long");
editor.putLong(key, Long.parseLong(value));
parser.nextTag();
parser.require(XmlPullParser.END_TAG, null, "long");
break;
case "float":
parser.require(XmlPullParser.START_TAG, null, "int");
editor.putFloat(key, Float.parseFloat(value));
parser.nextTag();
parser.require(XmlPullParser.END_TAG, null, "int");
break;
default:
parser.next();
}
}
} catch (IOException | XmlPullParserException e) {
e.printStackTrace();
}
editor.remove(Const.Key.ETAG_KEY);
editor.apply();
loadConfig();
config.delete();
}
}
}

View File

@@ -0,0 +1,219 @@
package com.topjohnwu.magisk;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends Activity
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
private final Handler mDrawerHandler = new Handler();
private int mDrawerItem;
private boolean fromShortcut = true;
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.nav_view) public NavigationView navigationView;
private float toolbarElevation;
@Override
public int getDarkTheme() {
return R.style.AppTheme_Dark;
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
MagiskManager mm = getMagiskManager();
if (!mm.hasInit) {
Intent intent = new Intent(this, SplashActivity.class);
String section = getIntent().getStringExtra(Const.Key.OPEN_SECTION);
if (section != null) {
intent.putExtra(Const.Key.OPEN_SECTION, section);
}
startActivity(intent);
finish();
}
String perm = getIntent().getStringExtra(Const.Key.INTENT_PERM);
if (perm != null) {
ActivityCompat.requestPermissions(this, new String[] { perm }, 0);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.magisk, R.string.magisk) {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
super.onDrawerSlide(drawerView, 0); // this disables the arrow @ completed tate
}
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, 0); // this disables the animation
}
};
toolbarElevation = toolbar.getElevation();
drawer.addDrawerListener(toggle);
toggle.syncState();
if (savedInstanceState == null)
navigate(getIntent().getStringExtra(Const.Key.OPEN_SECTION));
navigationView.setNavigationItemSelectedListener(this);
}
@Override
protected void onResume() {
super.onResume();
checkHideSection();
}
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen(navigationView)) {
drawer.closeDrawer(navigationView);
} else if (mDrawerItem != R.id.magisk && !fromShortcut) {
navigate(R.id.magisk);
} else {
finish();
}
}
@Override
public boolean onNavigationItemSelected(@NonNull final MenuItem menuItem) {
mDrawerHandler.removeCallbacksAndMessages(null);
mDrawerHandler.postDelayed(() -> navigate(menuItem.getItemId()), 250);
drawer.closeDrawer(navigationView);
return true;
}
@Override
public void onTopicPublished(Topic topic) {
recreate();
}
@Override
public Topic[] getSubscription() {
return new Topic[] { getMagiskManager().reloadActivity };
}
public void checkHideSection() {
MagiskManager mm = getMagiskManager();
Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible(
Shell.rootAccess() && mm.magiskVersionCode >= Const.MAGISK_VER.UNIFIED
&& mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
menu.findItem(R.id.modules).setVisible(!mm.prefs.getBoolean(Const.Key.COREONLY, false) &&
Shell.rootAccess() && mm.magiskVersionCode >= 0);
menu.findItem(R.id.downloads).setVisible(!mm.prefs.getBoolean(Const.Key.COREONLY, false)
&& Utils.checkNetworkStatus() && Shell.rootAccess() && mm.magiskVersionCode >= 0);
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
menu.findItem(R.id.superuser).setVisible(Shell.rootAccess() &&
!(Const.USER_ID > 0 && mm.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED));
}
public void navigate(String item) {
int itemId = R.id.magisk;
if (item != null) {
switch (item) {
case "superuser":
itemId = R.id.superuser;
break;
case "modules":
itemId = R.id.modules;
break;
case "downloads":
itemId = R.id.downloads;
break;
case "magiskhide":
itemId = R.id.magiskhide;
break;
case "log":
itemId = R.id.log;
break;
case "settings":
itemId = R.id.settings;
break;
case "about":
itemId = R.id.app_about;
break;
}
}
navigate(itemId);
}
public void navigate(int itemId) {
int bak = mDrawerItem;
mDrawerItem = itemId;
navigationView.setCheckedItem(itemId);
switch (itemId) {
case R.id.magisk:
fromShortcut = false;
displayFragment(new MagiskFragment(), true);
break;
case R.id.superuser:
displayFragment(new SuperuserFragment(), true);
break;
case R.id.modules:
displayFragment(new ModulesFragment(), true);
break;
case R.id.downloads:
displayFragment(new ReposFragment(), true);
break;
case R.id.magiskhide:
displayFragment(new MagiskHideFragment(), true);
break;
case R.id.log:
displayFragment(new LogFragment(), false);
break;
case R.id.settings:
startActivity(new Intent(this, SettingsActivity.class));
mDrawerItem = bak;
break;
case R.id.app_about:
startActivity(new Intent(this, AboutActivity.class));
mDrawerItem = bak;
break;
}
}
private void displayFragment(@NonNull Fragment navFragment, boolean setElevation) {
supportInvalidateOptionsMenu();
getSupportFragmentManager()
.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.replace(R.id.content_frame, navFragment)
.commitNow();
toolbar.setElevation(setElevation ? toolbarElevation : 0);
}
}

View File

@@ -0,0 +1,144 @@
package com.topjohnwu.magisk;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.asyncs.LoadModules;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
public class ModulesFragment extends Fragment implements Topic.Subscriber {
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@OnClick(R.id.fab)
public void selectFile() {
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("application/zip");
startActivityForResult(intent, Const.ID.FETCH_ZIP);
});
}
private List<Module> listModules = new ArrayList<>();
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_modules, container, false);
unbinder = ButterKnife.bind(this, view);
setHasOptionsMenu(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.GONE);
new LoadModules().exec();
});
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
mSwipeRefreshLayout.setEnabled(recyclerView.getChildAt(0).getTop() >= 0);
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
getActivity().setTitle(R.string.modules);
return view;
}
@Override
public void onTopicPublished(Topic topic) {
updateUI();
}
@Override
public Topic[] getSubscription() {
return new Topic[] { getApplication().moduleLoadDone };
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file
Intent intent = new Intent(getActivity(), FlashActivity.class);
intent.setData(data.getData()).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
startActivity(intent);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_reboot, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.reboot:
Shell.Async.su("/system/bin/reboot");
return true;
case R.id.reboot_recovery:
Shell.Async.su("/system/bin/reboot recovery");
return true;
case R.id.reboot_bootloader:
Shell.Async.su("/system/bin/reboot bootloader");
return true;
case R.id.reboot_download:
Shell.Async.su("/system/bin/reboot download");
return true;
default:
return false;
}
}
private void updateUI() {
listModules.clear();
listModules.addAll(getApplication().moduleMap.values());
if (listModules.size() == 0) {
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
recyclerView.setAdapter(new ModulesAdapter(listModules));
}
mSwipeRefreshLayout.setRefreshing(false);
}
}

View File

@@ -0,0 +1,26 @@
package com.topjohnwu.magisk;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.utils.Const;
public class NoUIActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] perms = getIntent().getStringArrayExtra(Const.Key.INTENT_PERM);
if (perms != null) {
ActivityCompat.requestPermissions(this, perms, 0);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
finish();
}
}

View File

@@ -0,0 +1,126 @@
package com.topjohnwu.magisk;
import android.app.AlertDialog;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SearchView;
import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Topic;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class ReposFragment extends Fragment implements Topic.Subscriber {
private Unbinder unbinder;
private MagiskManager mm;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
public static ReposAdapter adapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_repos, container, false);
unbinder = ButterKnife.bind(this, view);
mm = getApplication();
mSwipeRefreshLayout.setRefreshing(mm.repoLoadDone.isPending());
mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.VISIBLE);
emptyRv.setVisibility(View.GONE);
new UpdateRepos(true).exec();
});
getActivity().setTitle(R.string.downloads);
return view;
}
@Override
public void onResume() {
adapter = new ReposAdapter(mm.repoDB, mm.moduleMap);
recyclerView.setAdapter(adapter);
super.onResume();
}
@Override
public void onPause() {
super.onPause();
adapter = null;
}
@Override
public void onTopicPublished(Topic topic) {
mSwipeRefreshLayout.setRefreshing(false);
recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
}
@Override
public Topic[] getSubscription() {
return new Topic[] { mm.repoLoadDone };
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_repo, menu);
SearchView search = (SearchView) menu.findItem(R.id.repo_search).getActionView();
search.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
adapter.filter(newText);
return false;
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.repo_sort) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.sorting_order)
.setSingleChoiceItems(R.array.sorting_orders, mm.repoOrder, (d, which) -> {
mm.repoOrder = which;
mm.prefs.edit().putInt(Const.Key.REPO_ORDER, mm.repoOrder).apply();
adapter.notifyDBChanged();
d.dismiss();
}).show();
}
return true;
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -0,0 +1,321 @@
package com.topjohnwu.magisk;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.PreferenceScreen;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.HideManager;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.IOException;
import java.util.Locale;
import butterknife.BindView;
import butterknife.ButterKnife;
public class SettingsActivity extends Activity implements Topic.Subscriber {
@BindView(R.id.toolbar) Toolbar toolbar;
@Override
public int getDarkTheme() {
return R.style.AppTheme_StatusBar_Dark;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(R.string.settings);
ab.setDisplayHomeAsUpEnabled(true);
}
setFloating();
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().add(R.id.container, new SettingsFragment()).commit();
}
}
@Override
public void onTopicPublished(Topic topic) {
recreate();
}
@Override
public Topic[] getSubscription() {
return new Topic[] { getMagiskManager().reloadActivity };
}
public static class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener, Topic.Subscriber {
private SharedPreferences prefs;
private PreferenceScreen prefScreen;
private ListPreference updateChannel, suAccess, autoRes, suNotification,
requestTimeout, multiuserMode, namespaceMode;
private MagiskManager mm;
private PreferenceCategory generalCatagory;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.app_settings, rootKey);
mm = Utils.getMagiskManager(getActivity());
prefs = mm.prefs;
prefScreen = getPreferenceScreen();
generalCatagory = (PreferenceCategory) findPreference("general");
PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
Preference hideManager = findPreference("hide");
Preference restoreManager = findPreference("restore");
findPreference("clear").setOnPreferenceClickListener((pref) -> {
prefs.edit().remove(Const.Key.ETAG_KEY).apply();
mm.repoDB.clearRepo();
MagiskManager.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
return true;
});
updateChannel = (ListPreference) findPreference(Const.Key.UPDATE_CHANNEL);
suAccess = (ListPreference) findPreference(Const.Key.ROOT_ACCESS);
autoRes = (ListPreference) findPreference(Const.Key.SU_AUTO_RESPONSE);
requestTimeout = (ListPreference) findPreference(Const.Key.SU_REQUEST_TIMEOUT);
suNotification = (ListPreference) findPreference(Const.Key.SU_NOTIFICATION);
multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
updateChannel.setOnPreferenceChangeListener((pref, o) -> {
mm.updateChannel = Integer.parseInt((String) o);
if (mm.updateChannel == Const.Value.CUSTOM_CHANNEL) {
View v = LayoutInflater.from(getActivity()).inflate(R.layout.custom_channel_dialog, null);
EditText url = v.findViewById(R.id.custom_url);
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
new AlertDialog.Builder(getActivity())
.setTitle(R.string.settings_update_custom)
.setView(v)
.setPositiveButton(R.string.ok, (d, i) ->
prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
url.getText().toString()).apply())
.setNegativeButton(R.string.close, null)
.show();
}
return true;
});
setSummary();
// Disable dangerous settings in secondary user
if (Const.USER_ID > 0) {
suCategory.removePreference(multiuserMode);
}
// Disable re-authentication option on Android O, it will not work
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
reauth.setEnabled(false);
reauth.setSummary(R.string.android_o_not_support);
}
// Disable fingerprint option if not possible
if (!FingerprintHelper.canUseFingerprint()) {
fingerprint.setEnabled(false);
fingerprint.setSummary(R.string.disable_fingerprint);
}
if (mm.magiskVersionCode >= Const.MAGISK_VER.MANAGER_HIDE) {
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
hideManager.setOnPreferenceClickListener((pref) -> {
new HideManager(getActivity()).exec();
return true;
});
generalCatagory.removePreference(restoreManager);
} else {
if (Utils.checkNetworkStatus()) {
restoreManager.setOnPreferenceClickListener((pref) -> {
Utils.dlAndReceive(
getActivity(), new DownloadReceiver() {
@Override
public void onDownloadDone(Context context, Uri uri) {
mm.dumpPrefs();
if (ShellUtils.fastCmdResult("pm install " + uri.getPath()))
RootUtils.uninstallPkg(context.getPackageName());
}
},
mm.managerLink,
Utils.fmt("MagiskManager-v%s.apk", mm.remoteManagerVersionString)
);
return true;
});
} else {
generalCatagory.removePreference(restoreManager);
}
generalCatagory.removePreference(hideManager);
}
} else {
generalCatagory.removePreference(restoreManager);
generalCatagory.removePreference(hideManager);
}
if (!Shell.rootAccess() || (Const.USER_ID > 0 &&
mm.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
prefScreen.removePreference(suCategory);
}
if (!Shell.rootAccess()) {
prefScreen.removePreference(magiskCategory);
generalCatagory.removePreference(hideManager);
} else if (mm.magiskVersionCode < Const.MAGISK_VER.UNIFIED) {
prefScreen.removePreference(magiskCategory);
}
}
private void setLocalePreference(ListPreference lp) {
CharSequence[] entries = new CharSequence[mm.locales.size() + 1];
CharSequence[] entryValues = new CharSequence[mm.locales.size() + 1];
entries[0] = Utils.getLocaleString(MagiskManager.defaultLocale, R.string.system_default);
entryValues[0] = "";
int i = 1;
for (Locale locale : mm.locales) {
entries[i] = locale.getDisplayName(locale);
entryValues[i++] = locale.toLanguageTag();
}
lp.setEntries(entries);
lp.setEntryValues(entryValues);
lp.setSummary(MagiskManager.locale.getDisplayName(MagiskManager.locale));
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
prefs.registerOnSharedPreferenceChangeListener(this);
subscribeTopics();
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onDestroyView() {
prefs.unregisterOnSharedPreferenceChangeListener(this);
unsubscribeTopics();
super.onDestroyView();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
switch (key) {
case Const.Key.DARK_THEME:
mm.isDarkTheme = prefs.getBoolean(key, false);
mm.reloadActivity.publish(false);
return;
case Const.Key.COREONLY:
if (prefs.getBoolean(key, false)) {
try {
Const.MAGISK_DISABLE_FILE.createNewFile();
} catch (IOException ignored) {}
} else {
Const.MAGISK_DISABLE_FILE.delete();
}
Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
break;
case Const.Key.MAGISKHIDE:
if (prefs.getBoolean(key, false)) {
Shell.Async.su("magiskhide --enable");
} else {
Shell.Async.su("magiskhide --disable");
}
break;
case Const.Key.HOSTS:
if (prefs.getBoolean(key, false)) {
Shell.Async.su(
"cp -af /system/etc/hosts " + Const.MAGISK_HOST_FILE,
"mount -o bind " + Const.MAGISK_HOST_FILE + " /system/etc/hosts");
} else {
Shell.Async.su(
"umount -l /system/etc/hosts",
"rm -f " + Const.MAGISK_HOST_FILE);
}
break;
case Const.Key.ROOT_ACCESS:
case Const.Key.SU_MULTIUSER_MODE:
case Const.Key.SU_MNT_NS:
mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
break;
case Const.Key.LOCALE:
mm.setLocale();
mm.reloadActivity.publish(false);
break;
case Const.Key.UPDATE_CHANNEL:
new CheckUpdates().exec();
break;
case Const.Key.CHECK_UPDATES:
mm.setupUpdateCheck();
break;
}
mm.loadConfig();
setSummary();
}
private void setSummary() {
updateChannel.setSummary(getResources()
.getStringArray(R.array.update_channel)[mm.updateChannel]);
suAccess.setSummary(getResources()
.getStringArray(R.array.su_access)[mm.suAccessState]);
autoRes.setSummary(getResources()
.getStringArray(R.array.auto_response)[mm.suResponseType]);
suNotification.setSummary(getResources()
.getStringArray(R.array.su_notification)[mm.suNotificationType]);
requestTimeout.setSummary(
getString(R.string.request_timeout_summary, prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
multiuserMode.setSummary(getResources()
.getStringArray(R.array.multiuser_summary)[mm.multiuserMode]);
namespaceMode.setSummary(getResources()
.getStringArray(R.array.namespace_summary)[mm.suNamespaceMode]);
}
@Override
public void onTopicPublished(Topic topic) {
setLocalePreference((ListPreference) findPreference(Const.Key.LOCALE));
}
@Override
public Topic[] getSubscription() {
return new Topic[] { mm.localeDone };
}
}
}

View File

@@ -0,0 +1,88 @@
package com.topjohnwu.magisk;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.LoadModules;
import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
public class SplashActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RootUtils.init();
MagiskManager mm = getMagiskManager();
mm.repoDB = new RepoDatabaseHelper(this);
mm.loadMagiskInfo();
mm.getDefaultInstallFlags();
mm.loadPrefs();
// Dynamic detect all locales
new LoadLocale().exec();
// Create notification channel on Android O
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(Const.ID.NOTIFICATION_CHANNEL,
getString(R.string.magisk_updates), NotificationManager.IMPORTANCE_DEFAULT);
getSystemService(NotificationManager.class).createNotificationChannel(channel);
}
// Setup shortcuts
sendBroadcast(new Intent(this, ShortcutReceiver.class));
LoadModules loadModuleTask = new LoadModules();
if (Utils.checkNetworkStatus()) {
// Fire update check
new CheckUpdates().exec();
// Add repo update check
loadModuleTask.setCallBack(() -> new UpdateRepos(false).exec());
}
// Magisk working as expected
if (Shell.rootAccess() && mm.magiskVersionCode > 0) {
// Update check service
mm.setupUpdateCheck();
// Fire asynctasks
loadModuleTask.exec();
}
// Write back default values
mm.writeConfig();
mm.hasInit = true;
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION));
intent.putExtra(Const.Key.INTENT_PERM, getIntent().getStringExtra(Const.Key.INTENT_PERM));
startActivity(intent);
finish();
}
static class LoadLocale extends ParallelTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
MagiskManager.get().locales = Utils.getAvailableLocale();
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
MagiskManager.get().localeDone.publish();
}
}
}

View File

@@ -0,0 +1,89 @@
package com.topjohnwu.magisk;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.adapters.SuLogAdapter;
import com.topjohnwu.magisk.components.Fragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuLogFragment extends Fragment {
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
private Unbinder unbinder;
private MagiskManager mm;
private SuLogAdapter adapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu);
menu.findItem(R.id.menu_save).setVisible(false);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
unbinder = ButterKnife.bind(this, v);
mm = getApplication();
adapter = new SuLogAdapter(mm.mDB);
recyclerView.setAdapter(adapter);
updateList();
return v;
}
private void updateList() {
adapter.notifyDBChanged();
if (adapter.getSectionCount() == 0) {
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_refresh:
updateList();
return true;
case R.id.menu_clear:
mm.mDB.clearLogs();
updateList();
return true;
default:
return true;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -0,0 +1,63 @@
package com.topjohnwu.magisk;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.container.Policy;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuperuserFragment extends Fragment {
private Unbinder unbinder;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_superuser, container, false);
unbinder = ButterKnife.bind(this, view);
PackageManager pm = getActivity().getPackageManager();
MagiskManager mm = getApplication();
List<Policy> policyList = mm.mDB.getPolicyList(pm);
if (policyList.size() == 0) {
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
recyclerView.setAdapter(new PolicyAdapter(policyList, mm.mDB, pm));
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
return view;
}
@Override
public void onStart() {
super.onStart();
getActivity().setTitle(getString(R.string.superuser));
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -0,0 +1,158 @@
package com.topjohnwu.magisk.adapters;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.Filter;
import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
private List<ApplicationInfo> fullList, showList;
private List<String> hideList;
private PackageManager pm;
private ApplicationFilter filter;
public ApplicationAdapter() {
fullList = showList = Collections.emptyList();
hideList = Collections.emptyList();
filter = new ApplicationFilter();
pm = MagiskManager.get().getPackageManager();
new LoadApps().exec();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
return new ViewHolder(mView);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
ApplicationInfo info = showList.get(position);
holder.appIcon.setImageDrawable(info.loadIcon(pm));
holder.appName.setText(info.loadLabel(pm));
holder.appPackage.setText(info.packageName);
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(hideList.contains(info.packageName));
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked) {
Shell.Async.su("magiskhide --add " + info.packageName);
hideList.add(info.packageName);
} else {
Shell.Async.su("magiskhide --rm " + info.packageName);
hideList.remove(info.packageName);
}
});
}
@Override
public int getItemCount() {
return showList.size();
}
public void filter(String constraint) {
filter.filter(constraint);
}
public void refresh() {
new LoadApps().exec();
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.package_name) TextView appPackage;
@BindView(R.id.checkbox) CheckBox checkBox;
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
private class ApplicationFilter extends Filter {
private boolean lowercaseContains(String s, CharSequence filter) {
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint == null || constraint.length() == 0) {
showList = fullList;
} else {
showList = new ArrayList<>();
String filter = constraint.toString().toLowerCase();
for (ApplicationInfo info : fullList) {
if (lowercaseContains(info.loadLabel(pm).toString(), filter)
|| lowercaseContains(info.packageName, filter)) {
showList.add(info);
}
}
}
return null;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
notifyDataSetChanged();
}
}
private class LoadApps extends ParallelTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
fullList = pm.getInstalledApplications(0);
hideList = Shell.Sync.su("magiskhide --ls");
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next();
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) {
i.remove();
}
}
Collections.sort(fullList, (a, b) -> {
boolean ah = hideList.contains(a.packageName);
boolean bh = hideList.contains(b.packageName);
if (ah == bh) {
return a.loadLabel(pm).toString().toLowerCase().compareTo(
b.loadLabel(pm).toString().toLowerCase());
} else if (ah) {
return -1;
} else {
return 1;
}
});
return null;
}
@Override
protected void onPostExecute(Void v) {
MagiskManager.get().magiskHideDone.publish(false);
}
}
}

View File

@@ -12,9 +12,9 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.superuser.Shell;
import java.util.List;
@@ -32,7 +32,6 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false);
ButterKnife.bind(this, view);
return new ViewHolder(view);
}
@@ -41,52 +40,43 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
Context context = holder.itemView.getContext();
final Module module = mList.get(position);
holder.title.setText(module.getName());
holder.versionName.setText(module.getVersion());
String version = module.getVersion();
String author = module.getAuthor();
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.description.setText(module.getDescription());
String description = module.getDescription();
String noInfo = context.getString(R.string.no_info_provided);
holder.title.setText(module.getName());
holder.versionName.setText( TextUtils.isEmpty(version) ? noInfo : version);
holder.author.setText( TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
holder.description.setText( TextUtils.isEmpty(description) ? noInfo : description);
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(module.isEnabled());
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> new Async.RootTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
if (isChecked) {
module.removeDisableFile();
} else {
module.createDisableFile();
}
return null;
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
int snack;
if (isChecked) {
module.removeDisableFile();
snack = R.string.disable_file_removed;
} else {
module.createDisableFile();
snack = R.string.disable_file_created;
}
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
});
@Override
protected void onPostExecute(Void v) {
int title = isChecked ? R.string.disable_file_removed : R.string.disable_file_created;
Snackbar.make(holder.title, title, Snackbar.LENGTH_SHORT).show();
holder.delete.setOnClickListener(v -> {
boolean removed = module.willBeRemoved();
int snack;
if (removed) {
module.deleteRemoveFile();
snack = R.string.remove_file_deleted;
} else {
module.createRemoveFile();
snack = R.string.remove_file_created;
}
}.exec());
holder.delete.setOnClickListener(v -> new Async.RootTask<Void, Void, Void>() {
private final boolean removed = module.willBeRemoved();
@Override
protected Void doInBackground(Void... voids) {
if (removed) {
module.deleteRemoveFile();
} else {
module.createRemoveFile();
}
return null;
}
@Override
protected void onPostExecute(Void v) {
int title = removed ? R.string.remove_file_deleted : R.string.remove_file_created;
Snackbar.make(holder.title, title, Snackbar.LENGTH_SHORT).show();
updateDeleteButton(holder, module);
}
}.exec());
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
updateDeleteButton(holder, module);
});
if (module.isUpdated()) {
holder.notice.setVisibility(View.VISIBLE);

View File

@@ -0,0 +1,150 @@
package com.topjohnwu.magisk.adapters;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Switch;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import butterknife.BindView;
import butterknife.ButterKnife;
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
private List<Policy> policyList;
private MagiskDatabaseHelper dbHelper;
private PackageManager pm;
private Set<Policy> expandList = new HashSet<>();
public PolicyAdapter(List<Policy> list, MagiskDatabaseHelper db, PackageManager pm) {
policyList = list;
dbHelper = db;
this.pm = pm;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Policy policy = policyList.get(position);
holder.setExpanded(expandList.contains(policy));
holder.itemView.setOnClickListener(view -> {
if (holder.isExpanded()) {
holder.collapse();
expandList.remove(policy);
} else {
holder.expand();
expandList.add(policy);
}
});
holder.appName.setText(policy.appName);
holder.packageName.setText(policy.packageName);
holder.appIcon.setImageDrawable(policy.info.loadIcon(pm));
holder.masterSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if ((isChecked && policy.policy == Policy.DENY) ||
(!isChecked && policy.policy == Policy.ALLOW)) {
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.updatePolicy(policy);
}
});
holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if ((isChecked && !policy.notification) ||
(!isChecked && policy.notification)) {
policy.notification = isChecked;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_notif_on : R.string.su_snack_notif_off, policy.appName);
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.updatePolicy(policy);
}
});
holder.loggingSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if ((isChecked && !policy.logging) ||
(!isChecked && policy.logging)) {
policy.logging = isChecked;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_log_on : R.string.su_snack_log_off, policy.appName);
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.updatePolicy(policy);
}
});
holder.delete.setOnClickListener(v -> new AlertDialogBuilder((Activity) v.getContext())
.setTitle(R.string.su_revoke_title)
.setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName))
.setPositiveButton(R.string.yes, (dialog, which) -> {
policyList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, policyList.size());
SnackbarMaker.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName),
Snackbar.LENGTH_SHORT).show();
dbHelper.deletePolicy(policy);
})
.setNegativeButton(R.string.no_thanks, null)
.setCancelable(true)
.show());
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
holder.notificationSwitch.setChecked(policy.notification);
holder.loggingSwitch.setChecked(policy.logging);
// Hide for now
holder.moreInfo.setVisibility(View.GONE);
}
@Override
public int getItemCount() {
return policyList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder implements ExpandableView {
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.package_name) TextView packageName;
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.master_switch) Switch masterSwitch;
@BindView(R.id.notification_switch) Switch notificationSwitch;
@BindView(R.id.logging_switch) Switch loggingSwitch;
@BindView(R.id.expand_layout) ViewGroup expandLayout;
@BindView(R.id.delete) ImageView delete;
@BindView(R.id.more_info) ImageView moreInfo;
private Container container = new Container();
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
container.expandLayout = expandLayout;
setupExpandable();
}
@Override
public Container getContainer() {
return container;
}
}
}

View File

@@ -0,0 +1,192 @@
package com.topjohnwu.magisk.adapters;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, ReposAdapter.RepoHolder> {
private static final int UPDATES = 0;
private static final int INSTALLED = 1;
private static final int OTHERS = 2;
private Cursor repoCursor = null;
private Map<String, Module> moduleMap;
private RepoDatabaseHelper repoDB;
private List<Pair<Integer, List<Repo>>> repoPairs;
public ReposAdapter(RepoDatabaseHelper db, Map<String, Module> map) {
repoDB = db;
moduleMap = map;
repoPairs = new ArrayList<>();
notifyDBChanged();
}
@Override
public int getSectionCount() {
return repoPairs.size();
}
@Override
public int getItemCount(int section) {
return repoPairs.get(section).second.size();
}
@Override
public SectionHolder onCreateSectionViewHolder(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.section, parent, false);
return new SectionHolder(v);
}
@Override
public RepoHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_repo, parent, false);
return new RepoHolder(v);
}
@Override
public void onBindSectionViewHolder(SectionHolder holder, int section) {
switch (repoPairs.get(section).first) {
case UPDATES:
holder.sectionText.setText(R.string.update_available);
break;
case INSTALLED:
holder.sectionText.setText(R.string.installed);
break;
case OTHERS:
holder.sectionText.setText(R.string.not_installed);
break;
}
}
@Override
public void onBindItemViewHolder(RepoHolder holder, int section, int position) {
Repo repo = repoPairs.get(section).second.get(position);
Context context = holder.itemView.getContext();
holder.title.setText(repo.getName());
holder.versionName.setText(repo.getVersion());
String author = repo.getAuthor();
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.description.setText(repo.getDescription());
holder.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString()));
holder.infoLayout.setOnClickListener(v ->
new MarkDownWindow((Activity) context, null, repo.getDetailUrl()).exec());
holder.downloadImage.setOnClickListener(v -> {
String filename = repo.getName() + "-" + repo.getVersion() + ".zip";
new AlertDialogBuilder((Activity) context)
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
.setMessage(context.getString(R.string.repo_install_msg, filename))
.setCancelable(true)
.setPositiveButton(R.string.install, (d, i) ->
new ProcessRepoZip((Activity) context, repo.getZipUrl(),
Utils.getLegalFilename(filename), true).exec()
)
.setNeutralButton(R.string.download, (d, i) ->
new ProcessRepoZip((Activity) context, repo.getZipUrl(),
Utils.getLegalFilename(filename), false).exec())
.setNegativeButton(R.string.no_thanks, null)
.show();
});
}
public void notifyDBChanged() {
if (repoCursor != null)
repoCursor.close();
repoCursor = repoDB.getRepoCursor();
filter("");
}
public void filter(String s) {
List<Repo> updates = new ArrayList<>();
List<Repo> installed = new ArrayList<>();
List<Repo> others = new ArrayList<>();
repoPairs.clear();
while (repoCursor.moveToNext()) {
Repo repo = new Repo(repoCursor);
if (repo.getName().toLowerCase().contains(s.toLowerCase())
|| repo.getAuthor().toLowerCase().contains(s.toLowerCase())
|| repo.getDescription().toLowerCase().contains(s.toLowerCase())
) {
// Passed the repoFilter
Module module = moduleMap.get(repo.getId());
if (module != null) {
if (repo.getVersionCode() > module.getVersionCode()) {
// Updates
updates.add(repo);
} else {
installed.add(repo);
}
} else {
others.add(repo);
}
}
}
repoCursor.moveToFirst();
if (!updates.isEmpty())
repoPairs.add(new Pair<>(UPDATES, updates));
if (!installed.isEmpty())
repoPairs.add(new Pair<>(INSTALLED, installed));
if (!others.isEmpty())
repoPairs.add(new Pair<>(OTHERS, others));
notifyDataSetChanged();
}
static class SectionHolder extends RecyclerView.ViewHolder {
@BindView(R.id.section_text) TextView sectionText;
SectionHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
static class RepoHolder extends RecyclerView.ViewHolder {
@BindView(R.id.title) TextView title;
@BindView(R.id.version_name) TextView versionName;
@BindView(R.id.description) TextView description;
@BindView(R.id.author) TextView author;
@BindView(R.id.info_layout) LinearLayout infoLayout;
@BindView(R.id.download) ImageView downloadImage;
@BindView(R.id.update_time) TextView updateTime;
RepoHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}

View File

@@ -0,0 +1,93 @@
package com.topjohnwu.magisk.adapters;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
public abstract class SectionedAdapter<S extends RecyclerView.ViewHolder, C extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int SECTION_TYPE = Integer.MIN_VALUE;
@Override
final public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == SECTION_TYPE)
return onCreateSectionViewHolder(parent);
return onCreateItemViewHolder(parent, viewType);
}
@Override
@SuppressWarnings("unchecked")
final public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
PositionInfo info = getPositionInfo(position);
if (info.position == -1)
onBindSectionViewHolder((S) holder, info.section);
else
onBindItemViewHolder((C) holder, info.section, info.position);
}
@Override
final public int getItemCount() {
int size, sec;
size = sec = getSectionCount();
for (int i = 0; i < sec; ++i){
size += getItemCount(i);
}
return size;
}
@Override
final public int getItemViewType(int position) {
PositionInfo info = getPositionInfo(position);
if (info.position == -1)
return SECTION_TYPE;
else
return getItemViewType(info.section, info.position);
}
public int getItemViewType(int section, int position) {
return 0;
}
protected int getSectionPosition(int section) {
return getItemPosition(section, -1);
}
protected int getItemPosition(int section, int position) {
int realPosition = 0;
// Previous sections
for (int i = 0; i < section; ++i) {
realPosition += getItemCount(i) + 1;
}
// Current section
realPosition += position + 1;
return realPosition;
}
private PositionInfo getPositionInfo(int position) {
int section = 0;
while (true) {
if (position == 0)
return new PositionInfo(section, -1);
position -= 1;
if (position < getItemCount(section))
return new PositionInfo(section, position);
position -= getItemCount(section++);
}
}
private static class PositionInfo {
int section;
int position;
PositionInfo(int section, int position) {
this.section = section;
this.position = position;
}
}
public abstract int getSectionCount();
public abstract int getItemCount(int section);
public abstract S onCreateSectionViewHolder(ViewGroup parent);
public abstract C onCreateItemViewHolder(ViewGroup parent, int viewType);
public abstract void onBindSectionViewHolder(S holder, int section);
public abstract void onBindItemViewHolder(C holder, int section, int position);
}

View File

@@ -0,0 +1,155 @@
package com.topjohnwu.magisk.adapters;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import butterknife.BindView;
import butterknife.ButterKnife;
public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, SuLogAdapter.LogViewHolder> {
private List<List<Integer>> logEntryList;
private Set<Integer> itemExpanded, sectionExpanded;
private MagiskDatabaseHelper suDB;
private Cursor suLogCursor = null;
public SuLogAdapter(MagiskDatabaseHelper db) {
suDB = db;
logEntryList = Collections.emptyList();
sectionExpanded = new HashSet<>();
itemExpanded = new HashSet<>();
}
@Override
public int getSectionCount() {
return logEntryList.size();
}
@Override
public int getItemCount(int section) {
return sectionExpanded.contains(section) ? logEntryList.get(section).size() : 0;
}
@Override
public SectionHolder onCreateSectionViewHolder(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog_group, parent, false);
return new SectionHolder(v);
}
@Override
public LogViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog, parent, false);
return new LogViewHolder(v);
}
@Override
public void onBindSectionViewHolder(SectionHolder holder, int section) {
suLogCursor.moveToPosition(logEntryList.get(section).get(0));
SuLogEntry entry = new SuLogEntry(suLogCursor);
holder.arrow.setRotation(sectionExpanded.contains(section) ? 180 : 0);
holder.itemView.setOnClickListener(v -> {
RotateAnimation rotate;
if (sectionExpanded.contains(section)) {
holder.arrow.setRotation(0);
rotate = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
sectionExpanded.remove(section);
notifyItemRangeRemoved(getItemPosition(section, 0), logEntryList.get(section).size());
} else {
rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
sectionExpanded.add(section);
notifyItemRangeInserted(getItemPosition(section, 0), logEntryList.get(section).size());
}
rotate.setDuration(300);
rotate.setFillAfter(true);
holder.arrow.setAnimation(rotate);
});
holder.date.setText(entry.getDateString());
}
@Override
public void onBindItemViewHolder(LogViewHolder holder, int section, int position) {
int sqlPosition = logEntryList.get(section).get(position);
suLogCursor.moveToPosition(sqlPosition);
SuLogEntry entry = new SuLogEntry(suLogCursor);
holder.setExpanded(itemExpanded.contains(sqlPosition));
holder.itemView.setOnClickListener(view -> {
if (holder.isExpanded()) {
holder.collapse();
itemExpanded.remove(sqlPosition);
} else {
holder.expand();
itemExpanded.add(sqlPosition);
}
});
holder.appName.setText(entry.appName);
holder.action.setText(entry.action ? R.string.grant : R.string.deny);
holder.command.setText(entry.command);
holder.fromPid.setText(String.valueOf(entry.fromPid));
holder.toUid.setText(String.valueOf(entry.toUid));
holder.time.setText(entry.getTimeString());
}
public void notifyDBChanged() {
if (suLogCursor != null)
suLogCursor.close();
suLogCursor = suDB.getLogCursor();
logEntryList = suDB.getLogStructure();
itemExpanded.clear();
sectionExpanded.clear();
sectionExpanded.add(0);
notifyDataSetChanged();
}
static class SectionHolder extends RecyclerView.ViewHolder {
@BindView(R.id.date) TextView date;
@BindView(R.id.arrow) ImageView arrow;
SectionHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
static class LogViewHolder extends RecyclerView.ViewHolder implements ExpandableView {
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.action) TextView action;
@BindView(R.id.time) TextView time;
@BindView(R.id.fromPid) TextView fromPid;
@BindView(R.id.toUid) TextView toUid;
@BindView(R.id.command) TextView command;
@BindView(R.id.expand_layout) ViewGroup expandLayout;
private Container container = new Container();
LogViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
container.expandLayout = expandLayout;
setupExpandable();
}
@Override
public Container getContainer() {
return container;
}
}
}

View File

@@ -0,0 +1,41 @@
package com.topjohnwu.magisk.adapters;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.ArrayList;
import java.util.List;
public class TabFragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;
private List<String> titleList;
public TabFragmentAdapter(FragmentManager fm) {
super(fm);
fragmentList = new ArrayList<>();
titleList = new ArrayList<>();
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return titleList.get(position);
}
public void addTab(Fragment fragment, String title) {
fragmentList.add(fragment);
titleList.add(title);
}
}

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