1
mirror of https://github.com/topjohnwu/Magisk synced 2025-11-04 17:32:31 +01:00

Compare commits

..

141 Commits

Author SHA1 Message Date
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
153 changed files with 5451 additions and 3915 deletions

View File

@@ -1,15 +1,15 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 26 compileSdkVersion 27
buildToolsVersion "26.0.1" buildToolsVersion "27.0.1"
defaultConfig { defaultConfig {
applicationId "com.topjohnwu.magisk" applicationId "com.topjohnwu.magisk"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 26 targetSdkVersion 27
versionCode 54 versionCode 64
versionName "5.3.0" versionName "5.4.3"
ndk { ndk {
moduleName 'zipadjust' moduleName 'zipadjust'
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
@@ -45,24 +45,22 @@ android {
disable 'MissingTranslation' disable 'MissingTranslation'
} }
} }
repositories { repositories {
jcenter() jcenter()
google()
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
maven { url "https://maven.google.com" }
} }
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':resource') implementation project(':crypto')
implementation 'com.android.support:recyclerview-v7:26.0.2' implementation 'com.android.support:recyclerview-v7:27.0.1'
implementation 'com.android.support:cardview-v7:26.0.2' implementation 'com.android.support:cardview-v7:27.0.1'
implementation 'com.android.support:design:26.0.2' implementation 'com.android.support:design:27.0.1'
implementation 'com.android.support:support-v4:26.0.2' implementation 'com.android.support:support-v4:27.0.1'
implementation 'com.jakewharton:butterknife:8.8.1' implementation 'com.jakewharton:butterknife:8.8.1'
implementation 'com.atlassian.commonmark:commonmark:0.9.0' implementation 'com.atlassian.commonmark:commonmark:0.10.0'
implementation 'org.bouncycastle:bcprov-jdk15on:1.57'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.57'
implementation 'org.kamranzafar:jtar:2.3' implementation 'org.kamranzafar:jtar:2.3'
implementation 'com.google.android.gms:play-services-safetynet:9.0.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
} }

View File

@@ -16,10 +16,9 @@
# public *; # public *;
#} #}
# Keep all names, we are open source anyway :)
-keepnames class ** { *; }
# BouncyCastle # BouncyCastle
-keep class org.bouncycastle.** { *; } -keep class org.bouncycastle.jcajce.provider.** { *; }
-dontwarn javax.naming.** -dontwarn javax.naming.**
-dontwarn android.content.**
-dontwarn android.animation.**

View File

@@ -14,10 +14,12 @@
android:name=".MagiskManager" android:name=".MagiskManager"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:ignore="AllowBackup,GoogleAppIndexingWarning"> android:directBootAware="true"
tools:ignore="UnusedAttribute">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
@@ -72,6 +74,7 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".receivers.ManagerUpdate" /> <receiver android:name=".receivers.ManagerUpdate" />
<receiver android:name=".receivers.RebootReceiver" />
<service android:name=".services.OnBootIntentService" /> <service android:name=".services.OnBootIntentService" />
<service <service
@@ -89,9 +92,10 @@
android:resource="@xml/file_paths" /> android:resource="@xml/file_paths" />
</provider> </provider>
<!-- Hardcode GMS version -->
<meta-data <meta-data
android:name="com.google.android.gms.version" android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" /> android:value="11717000" />
</application> </application>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -17,19 +17,17 @@ import android.widget.TextView;
import com.topjohnwu.magisk.components.AboutCardRow; import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.Activity; import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.components.AlertDialogBuilder; import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.utils.Const;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Locale;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
public class AboutActivity extends Activity { public class AboutActivity extends Activity {
private static final String DONATION_URL = "https://www.paypal.me/topjohnwu";
private static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
private static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/MagiskManager";
@BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.app_version_info) AboutCardRow appVersionInfo; @BindView(R.id.app_version_info) AboutCardRow appVersionInfo;
@BindView(R.id.app_changelog) AboutCardRow appChangelog; @BindView(R.id.app_changelog) AboutCardRow appChangelog;
@@ -57,7 +55,7 @@ public class AboutActivity extends Activity {
ab.setDisplayHomeAsUpEnabled(true); ab.setDisplayHomeAsUpEnabled(true);
} }
appVersionInfo.setSummary(BuildConfig.VERSION_NAME); appVersionInfo.setSummary(String.format(Locale.US, "%s (%d)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
String changes = null; String changes = null;
try (InputStream is = getAssets().open("changelog.html")) { try (InputStream is = getAssets().open("changelog.html")) {
@@ -119,13 +117,13 @@ public class AboutActivity extends Activity {
} }
appSourceCode.removeSummary(); appSourceCode.removeSummary();
appSourceCode.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(SOURCE_CODE_URL)))); appSourceCode.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Const.Url.SOURCE_CODE_URL))));
supportThread.removeSummary(); supportThread.removeSummary();
supportThread.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(XDA_THREAD)))); supportThread.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Const.Url.XDA_THREAD))));
donation.removeSummary(); donation.removeSummary();
donation.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(DONATION_URL)))); donation.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Const.Url.DONATION_URL))));
setFloating(); setFloating();
} }

View File

@@ -4,22 +4,29 @@ import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater; import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.FlashZip; import com.topjohnwu.magisk.asyncs.FlashZip;
import com.topjohnwu.magisk.asyncs.InstallMagisk; import com.topjohnwu.magisk.asyncs.InstallMagisk;
import com.topjohnwu.magisk.components.Activity; import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.utils.AdaptiveList; import com.topjohnwu.magisk.container.CallbackList;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.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.List;
import java.util.Locale;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
@@ -27,28 +34,45 @@ import butterknife.OnClick;
public class FlashActivity extends Activity { public class FlashActivity extends Activity {
public static final String SET_ACTION = "action";
public static final String SET_BOOT = "boot";
public static final String SET_ENC = "enc";
public static final String SET_VERITY = "verity";
public static final String FLASH_ZIP = "flash";
public static final String PATCH_BOOT = "patch";
public static final String FLASH_MAGISK = "magisk";
@BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.flash_logs) RecyclerView flashLogs; @BindView(R.id.txtLog) TextView flashLogs;
@BindView(R.id.button_panel) LinearLayout buttonPanel; @BindView(R.id.button_panel) public LinearLayout buttonPanel;
@BindView(R.id.reboot) Button reboot; @BindView(R.id.reboot) public Button reboot;
@BindView(R.id.scrollView) ScrollView sv;
private List<String> logs;
@OnClick(R.id.no_thanks) @OnClick(R.id.no_thanks)
public void dismiss() { void dismiss() {
finish(); finish();
} }
@OnClick(R.id.reboot) @OnClick(R.id.reboot)
public void reboot() { void reboot() {
getShell().su_raw("reboot"); Shell.su_raw("/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 @Override
@@ -56,55 +80,45 @@ public class FlashActivity extends Activity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flash); setContentView(R.layout.activity_flash);
ButterKnife.bind(this); ButterKnife.bind(this);
AdaptiveList<String> rootShellOutput = new AdaptiveList<>(flashLogs);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar(); ActionBar ab = getSupportActionBar();
if (ab != null) { if (ab != null) {
ab.setTitle(R.string.flashing); ab.setTitle(R.string.flashing);
} }
setFloating(); setFloating();
setFinishOnTouchOutside(false);
if (!Shell.rootAccess()) if (!Shell.rootAccess())
reboot.setVisibility(View.GONE); reboot.setVisibility(View.GONE);
flashLogs.setAdapter(new FlashLogAdapter(rootShellOutput)); logs = new ArrayList<>();
List<String> console = new CallbackList<String>() {
@Override
public synchronized void onAddElement(String e) {
logs.add(e);
flashLogs.setText(TextUtils.join("\n", this));
sv.postDelayed(() -> sv.fullScroll(ScrollView.FOCUS_DOWN), 10);
}
};
// We must receive a Uri of the target zip // We must receive a Uri of the target zip
Intent intent = getIntent(); Intent intent = getIntent();
Uri uri = intent.getData(); Uri uri = intent.getData();
boolean keepEnc = intent.getBooleanExtra(SET_ENC, false); boolean keepEnc = intent.getBooleanExtra(Const.Key.FLASH_SET_ENC, false);
boolean keepVerity = intent.getBooleanExtra(SET_VERITY, false); boolean keepVerity = intent.getBooleanExtra(Const.Key.FLASH_SET_VERITY, false);
switch (getIntent().getStringExtra(SET_ACTION)) { switch (intent.getStringExtra(Const.Key.FLASH_ACTION)) {
case FLASH_ZIP: case Const.Value.FLASH_ZIP:
new FlashZip(this, uri, rootShellOutput) new FlashZip(this, uri, console, logs).exec();
.setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE)) break;
case Const.Value.PATCH_BOOT:
new InstallMagisk(this, console, logs, uri, keepEnc, keepVerity, (Uri) intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT))
.exec(); .exec();
break; break;
case PATCH_BOOT: case Const.Value.FLASH_MAGISK:
new InstallMagisk(this, rootShellOutput, uri, keepEnc, keepVerity, (Uri) intent.getParcelableExtra(SET_BOOT)) new InstallMagisk(this, console, logs, uri, keepEnc, keepVerity, intent.getStringExtra(Const.Key.FLASH_SET_BOOT))
.setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE))
.exec(); .exec();
break; break;
case FLASH_MAGISK:
String boot = intent.getStringExtra(SET_BOOT);
if (getMagiskManager().remoteMagiskVersionCode < 1370) {
// Use legacy installation method
getShell().su_raw(
"echo \"BOOTIMAGE=" + boot + "\" > /dev/.magisk",
"echo \"KEEPFORCEENCRYPT=" + keepEnc + "\" >> /dev/.magisk",
"echo \"KEEPVERITY=" + keepVerity + "\" >> /dev/.magisk"
);
new FlashZip(this, uri, rootShellOutput)
.setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE))
.exec();
} else {
// Use new installation method
new InstallMagisk(this, rootShellOutput, uri, keepEnc, keepVerity, boot)
.setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE))
.exec();
}
break;
} }
} }
@@ -112,40 +126,4 @@ public class FlashActivity extends Activity {
public void onBackPressed() { public void onBackPressed() {
// Prevent user accidentally press back button // Prevent user accidentally press back button
} }
private static class FlashLogAdapter extends RecyclerView.Adapter<ViewHolder> {
private List<String> mList;
FlashLogAdapter(List<String> list) {
mList = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_flashlog, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.text.setText(mList.get(position));
}
@Override
public int getItemCount() {
return mList.size();
}
}
public static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.textView) TextView text;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
} }

View File

@@ -33,13 +33,10 @@ public class LogFragment extends Fragment {
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager()); TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
if (getApplication().isSuClient) { adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
tab.setupWithViewPager(viewPager);
tab.setVisibility(View.VISIBLE);
}
adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk)); adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
tab.setupWithViewPager(viewPager);
tab.setVisibility(View.VISIBLE);
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);

File diff suppressed because it is too large Load Diff

View File

@@ -84,7 +84,7 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
} }
@Override @Override
public void onTopicPublished(Topic topic) { public void onTopicPublished(Topic topic, Object result) {
mSwipeRefreshLayout.setRefreshing(false); mSwipeRefreshLayout.setRefreshing(false);
appAdapter.filter(lastFilter); appAdapter.filter(lastFilter);
} }

View File

@@ -1,9 +1,7 @@
package com.topjohnwu.magisk; package com.topjohnwu.magisk;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.text.TextUtils; import android.text.TextUtils;
@@ -22,6 +20,7 @@ import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.ParallelTask; import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.components.SnackbarMaker; import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
@@ -29,6 +28,7 @@ import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Locale;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
@@ -36,8 +36,6 @@ import butterknife.Unbinder;
public class MagiskLogFragment extends Fragment { public class MagiskLogFragment extends Fragment {
private static final String MAGISK_LOG = "/cache/magisk.log";
private Unbinder unbinder; private Unbinder unbinder;
@BindView(R.id.txtLog) TextView txtLog; @BindView(R.id.txtLog) TextView txtLog;
@@ -110,30 +108,29 @@ public class MagiskLogFragment extends Fragment {
super(MagiskLogFragment.this.getActivity()); super(MagiskLogFragment.this.getActivity());
} }
@SuppressLint("DefaultLocale")
@Override @Override
protected Object doInBackground(Object... params) { protected Object doInBackground(Object... params) {
mode = (int) params[0]; mode = (int) params[0];
switch (mode) { switch (mode) {
case 0: case 0:
StringBuildingList logList = new StringBuildingList(); StringBuildingList logList = new StringBuildingList();
getShell().su(logList, "cat " + MAGISK_LOG); Shell.su(logList, "cat " + Const.MAGISK_LOG + " | tail -n 1000");
return logList.toString(); return logList.getCharSequence();
case 1: case 1:
getShell().su_raw("echo -n > " + MAGISK_LOG); Shell.su_raw("echo -n > " + Const.MAGISK_LOG);
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show(); SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
return ""; return "";
case 2: case 2:
Calendar now = Calendar.getInstance(); Calendar now = Calendar.getInstance();
String filename = String.format( String filename = String.format(Locale.US,
"magisk_%s_%04d%02d%02d_%02d%02d%02d.log", "error", "magisk_log_%04d%02d%02d_%02d:%02d:%02d.log",
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1, now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE), now.get(Calendar.SECOND)); now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
targetFile = new File(Environment.getExternalStorageDirectory() + "/MagiskManager/" + filename); targetFile = new File(Const.EXTERNAL_PATH + "/logs", filename);
if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs()) if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs())
|| (targetFile.exists() && !targetFile.delete())) { || (targetFile.exists() && !targetFile.delete())) {
@@ -142,7 +139,7 @@ public class MagiskLogFragment extends Fragment {
try (FileWriter out = new FileWriter(targetFile)) { try (FileWriter out = new FileWriter(targetFile)) {
FileWritingList fileWritingList = new FileWritingList(out); FileWritingList fileWritingList = new FileWritingList(out);
getShell().su(fileWritingList, "cat " + MAGISK_LOG); Shell.su(fileWritingList, "cat " + Const.MAGISK_LOG);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return false; return false;
@@ -158,21 +155,21 @@ public class MagiskLogFragment extends Fragment {
switch (mode) { switch (mode) {
case 0: case 0:
case 1: case 1:
String llog = (String) o; CharSequence llog = (CharSequence) o;
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
if (TextUtils.isEmpty(llog)) if (TextUtils.isEmpty(llog))
txtLog.setText(R.string.log_is_empty); txtLog.setText(R.string.log_is_empty);
else else
txtLog.setText(llog); txtLog.setText(llog);
svLog.post(() -> svLog.scrollTo(0, txtLog.getHeight())); svLog.postDelayed(() -> svLog.fullScroll(ScrollView.FOCUS_DOWN), 100);
hsvLog.post(() -> hsvLog.scrollTo(0, 0)); hsvLog.postDelayed(() -> hsvLog.fullScroll(ScrollView.FOCUS_LEFT), 100);
break; break;
case 2: case 2:
boolean bool = (boolean) o; boolean bool = (boolean) o;
if (bool) { if (bool) {
getMagiskManager().toast(targetFile.toString(), Toast.LENGTH_LONG); MagiskManager.toast(targetFile.getPath(), Toast.LENGTH_LONG);
} else { } else {
getMagiskManager().toast(R.string.logs_save_failed, Toast.LENGTH_LONG); MagiskManager.toast(R.string.logs_save_failed, Toast.LENGTH_LONG);
} }
break; break;
} }
@@ -205,9 +202,8 @@ public class MagiskLogFragment extends Fragment {
return true; return true;
} }
@Override public CharSequence getCharSequence() {
public String toString() { return builder;
return builder.toString();
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView; import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout;
@@ -16,6 +17,7 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import com.topjohnwu.magisk.components.Activity; import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
@@ -38,11 +40,27 @@ public class MainActivity extends Activity
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
getMagiskManager().startup();
prefs = getMagiskManager().prefs; MagiskManager mm = getMagiskManager();
if (getMagiskManager().isDarkTheme) { 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);
}
prefs = mm.prefs;
if (mm.isDarkTheme) {
setTheme(R.style.AppTheme_Dark); setTheme(R.style.AppTheme_Dark);
} }
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -70,10 +88,9 @@ public class MainActivity extends Activity
toggle.syncState(); toggle.syncState();
if (savedInstanceState == null) if (savedInstanceState == null)
navigate(getIntent().getStringExtra(MagiskManager.INTENT_SECTION)); navigate(getIntent().getStringExtra(Const.Key.OPEN_SECTION));
navigationView.setNavigationItemSelectedListener(this); navigationView.setNavigationItemSelectedListener(this);
} }
@Override @Override
@@ -102,7 +119,7 @@ public class MainActivity extends Activity
} }
@Override @Override
public void onTopicPublished(Topic topic) { public void onTopicPublished(Topic topic, Object result) {
recreate(); recreate();
} }
@@ -112,17 +129,18 @@ public class MainActivity extends Activity
} }
public void checkHideSection() { public void checkHideSection() {
MagiskManager mm = getMagiskManager();
Menu menu = navigationView.getMenu(); Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible( menu.findItem(R.id.magiskhide).setVisible(
Shell.rootAccess() && getMagiskManager().magiskVersionCode >= 1300 Shell.rootAccess() && mm.magiskVersionCode >= 1300
&& prefs.getBoolean("magiskhide", false)); && prefs.getBoolean(Const.Key.MAGISKHIDE, false));
menu.findItem(R.id.modules).setVisible( menu.findItem(R.id.modules).setVisible(
Shell.rootAccess() && getMagiskManager().magiskVersionCode >= 0); Shell.rootAccess() && mm.magiskVersionCode >= 0);
menu.findItem(R.id.downloads).setVisible(Utils.checkNetworkStatus(this) && menu.findItem(R.id.downloads).setVisible(Utils.checkNetworkStatus() &&
Shell.rootAccess() && getMagiskManager().magiskVersionCode >= 0); Shell.rootAccess() && mm.magiskVersionCode >= 0);
menu.setGroupVisible(R.id.second_group, !mm.coreOnly);
menu.findItem(R.id.log).setVisible(Shell.rootAccess()); menu.findItem(R.id.log).setVisible(Shell.rootAccess());
menu.findItem(R.id.superuser).setVisible( menu.findItem(R.id.superuser).setVisible(Shell.rootAccess());
Shell.rootAccess() && getMagiskManager().isSuClient);
} }
public void navigate(String item) { public void navigate(String item) {
@@ -176,7 +194,7 @@ public class MainActivity extends Activity
displayFragment(new ReposFragment(), "downloads", true); displayFragment(new ReposFragment(), "downloads", true);
break; break;
case R.id.magiskhide: case R.id.magiskhide:
displayFragment(new MagiskHideFragment(), "magiskhide", true); displayFragment(new MagiskHideFragment(), Const.Key.MAGISKHIDE, true);
break; break;
case R.id.log: case R.id.log:
displayFragment(new LogFragment(), "log", false); displayFragment(new LogFragment(), "log", false);

View File

@@ -1,5 +1,6 @@
package com.topjohnwu.magisk; package com.topjohnwu.magisk;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
@@ -14,9 +15,10 @@ import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ModulesAdapter; import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.asyncs.LoadModules; import com.topjohnwu.magisk.asyncs.LoadModules;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.module.Module; import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -28,17 +30,17 @@ import butterknife.Unbinder;
public class ModulesFragment extends Fragment implements Topic.Subscriber { public class ModulesFragment extends Fragment implements Topic.Subscriber {
private static final int FETCH_ZIP_CODE = 2;
private Unbinder unbinder; private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView; @BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv; @BindView(R.id.empty_rv) TextView emptyRv;
@OnClick(R.id.fab) @OnClick(R.id.fab)
public void selectFile() { public void selectFile() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT); Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> {
intent.setType("application/zip"); Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, FETCH_ZIP_CODE); intent.setType("application/zip");
startActivityForResult(intent, Const.ID.FETCH_ZIP);
});
} }
private List<Module> listModules = new ArrayList<>(); private List<Module> listModules = new ArrayList<>();
@@ -51,7 +53,7 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
mSwipeRefreshLayout.setOnRefreshListener(() -> { mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.GONE); recyclerView.setVisibility(View.GONE);
new LoadModules(getActivity()).exec(); new LoadModules().exec();
}); });
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@@ -72,8 +74,7 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
} }
@Override @Override
public void onTopicPublished(Topic topic) { public void onTopicPublished(Topic topic, Object result) {
Logger.dev("ModulesFragment: UI refresh triggered");
updateUI(); updateUI();
} }
@@ -84,10 +85,10 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FETCH_ZIP_CODE && resultCode == Activity.RESULT_OK && data != null) { if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file // Get the URI of the selected file
Intent intent = new Intent(getActivity(), FlashActivity.class); Intent intent = new Intent(getActivity(), FlashActivity.class);
intent.setData(data.getData()).putExtra(FlashActivity.SET_ACTION, FlashActivity.FLASH_ZIP); intent.setData(data.getData()).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
startActivity(intent); startActivity(intent);
} }
} }

View File

@@ -15,7 +15,6 @@ import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ReposAdapter; import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.asyncs.UpdateRepos; import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import butterknife.BindView; import butterknife.BindView;
@@ -29,7 +28,7 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
@BindView(R.id.empty_rv) TextView emptyRv; @BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
private ReposAdapter adapter; public static ReposAdapter adapter;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -43,14 +42,12 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
View view = inflater.inflate(R.layout.fragment_repos, container, false); View view = inflater.inflate(R.layout.fragment_repos, container, false);
unbinder = ButterKnife.bind(this, view); unbinder = ButterKnife.bind(this, view);
adapter = new ReposAdapter(getApplication().repoDB, getApplication().moduleMap);
recyclerView.setAdapter(adapter);
mSwipeRefreshLayout.setRefreshing(true); mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> { mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE);
new UpdateRepos(getActivity()).exec(); emptyRv.setVisibility(View.GONE);
new UpdateRepos(true).exec();
}); });
getActivity().setTitle(R.string.downloads); getActivity().setTitle(R.string.downloads);
@@ -59,10 +56,21 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
} }
@Override @Override
public void onTopicPublished(Topic topic) { public void onResume() {
Logger.dev("ReposFragment: UI refresh triggered"); adapter = new ReposAdapter(getApplication().repoDB, getApplication().moduleMap);
recyclerView.setAdapter(adapter);
super.onResume();
}
@Override
public void onPause() {
super.onPause();
adapter = null;
}
@Override
public void onTopicPublished(Topic topic, Object result) {
mSwipeRefreshLayout.setRefreshing(false); mSwipeRefreshLayout.setRefreshing(false);
adapter.notifyDBChanged();
recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE); recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,27 @@
package com.topjohnwu.magisk; package com.topjohnwu.magisk;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.os.Bundle; 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.components.Activity;
import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import java.util.List;
public class SplashActivity extends Activity { public class SplashActivity extends Activity {
@@ -11,14 +29,106 @@ public class SplashActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getMagiskManager().startup(); MagiskManager mm = getMagiskManager();
// 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);
}
mm.loadMagiskInfo();
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) {
List<String> ret = Shell.su("echo \"$BOOTIMAGE\"");
if (Utils.isValidShellResponse(ret)) {
mm.bootBlock = ret.get(0);
}
// Setup suDB
SuDatabaseHelper.setupSuDB();
// Check alternative Magisk Manager
String pkg;
if (getPackageName().equals(Const.ORIG_PKG_NAME) &&
(pkg = mm.suDB.getStrings(Const.Key.SU_REQUESTER, null)) != null) {
Shell.su_raw("pm uninstall " + pkg);
mm.suDB.setStrings(Const.Key.SU_REQUESTER, null);
}
// Add update checking service
if (Const.Value.UPDATE_SERVICE_VER > mm.updateServiceVersion) {
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();
((JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(info);
mm.updateServiceVersion = Const.Value.UPDATE_SERVICE_VER;
}
// Fire asynctasks
loadModuleTask.exec();
// Check dtbo status
Utils.patchDTBO();
}
// Write back default values
mm.prefs.edit()
.putBoolean(Const.Key.DARK_THEME, mm.isDarkTheme)
.putBoolean(Const.Key.MAGISKHIDE, mm.magiskHide)
.putBoolean(Const.Key.UPDATE_NOTIFICATION, mm.updateNotification)
.putBoolean(Const.Key.HOSTS, Utils.itemExist(Const.MAGISK_HOST_FILE()))
.putBoolean(Const.Key.DISABLE, Utils.itemExist(Const.MAGISK_DISABLE_FILE))
.putBoolean(Const.Key.SU_REAUTH, mm.suReauth)
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(mm.suRequestTimeout))
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(mm.suResponseType))
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(mm.suNotificationType))
.putString(Const.Key.ROOT_ACCESS, String.valueOf(mm.suAccessState))
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(mm.multiuserMode))
.putString(Const.Key.SU_MNT_NS, String.valueOf(mm.suNamespaceMode))
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(mm.updateChannel))
.putString(Const.Key.LOCALE, mm.localeConfig)
.putString(Const.Key.BOOT_FORMAT, mm.bootFormat)
.putInt(Const.Key.UPDATE_SERVICE_VER, mm.updateServiceVersion)
.apply();
mm.hasInit = true;
Intent intent = new Intent(this, MainActivity.class); Intent intent = new Intent(this, MainActivity.class);
String section = getIntent().getStringExtra(MagiskManager.INTENT_SECTION); intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION));
if (section != null) { intent.putExtra(Const.Key.INTENT_PERM, getIntent().getStringExtra(Const.Key.INTENT_PERM));
intent.putExtra(MagiskManager.INTENT_SECTION, section);
}
startActivity(intent); startActivity(intent);
finish(); 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

@@ -11,7 +11,7 @@ import android.widget.TextView;
import com.topjohnwu.magisk.adapters.PolicyAdapter; import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.superuser.Policy; import com.topjohnwu.magisk.container.Policy;
import java.util.List; import java.util.List;

View File

@@ -5,6 +5,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -16,12 +17,12 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.ParallelTask; import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.components.SnackbarMaker; import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@@ -31,23 +32,11 @@ import butterknife.ButterKnife;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> { public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
public static final List<String> BLACKLIST = Arrays.asList(
"android",
"com.topjohnwu.magisk",
"com.google.android.gms"
);
private static final List<String> SNLIST = Arrays.asList(
"com.google.android.apps.walletnfcrel",
"com.nianticlabs.pokemongo"
);
private List<ApplicationInfo> mOriginalList, mList; private List<ApplicationInfo> mOriginalList, mList;
private List<String> mHideList; private List<String> mHideList;
private PackageManager pm; private PackageManager pm;
private ApplicationFilter filter; private ApplicationFilter filter;
private Topic magiskHideDone; private Topic magiskHideDone;
private Shell shell;
public ApplicationAdapter(Context context) { public ApplicationAdapter(Context context) {
mOriginalList = mList = Collections.emptyList(); mOriginalList = mList = Collections.emptyList();
@@ -55,10 +44,13 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
filter = new ApplicationFilter(); filter = new ApplicationFilter();
pm = context.getPackageManager(); pm = context.getPackageManager();
magiskHideDone = Utils.getMagiskManager(context).magiskHideDone; magiskHideDone = Utils.getMagiskManager(context).magiskHideDone;
shell = Shell.getShell(context);
new LoadApps().exec(); new LoadApps().exec();
} }
private boolean lowercaseContains(CharSequence string, CharSequence nonNullLowercaseSearch) {
return !TextUtils.isEmpty(string) && string.toString().toLowerCase().contains(nonNullLowercaseSearch);
}
@Override @Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false); View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
@@ -77,7 +69,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
holder.itemView.setOnClickListener(null); holder.itemView.setOnClickListener(null);
holder.checkBox.setOnCheckedChangeListener(null); holder.checkBox.setOnCheckedChangeListener(null);
if (SNLIST.contains(info.packageName)) { if (Const.SN_DEFAULTLIST.contains(info.packageName)) {
holder.checkBox.setChecked(true); holder.checkBox.setChecked(true);
holder.checkBox.setEnabled(false); holder.checkBox.setEnabled(false);
holder.itemView.setOnClickListener(v -> holder.itemView.setOnClickListener(v ->
@@ -89,10 +81,10 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
holder.checkBox.setChecked(mHideList.contains(info.packageName)); holder.checkBox.setChecked(mHideList.contains(info.packageName));
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> { holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked) { if (isChecked) {
Utils.addMagiskHide(shell, info.packageName); Shell.su_raw("magiskhide --add " + info.packageName);
mHideList.add(info.packageName); mHideList.add(info.packageName);
} else { } else {
Utils.rmMagiskHide(shell, info.packageName); Shell.su_raw("magiskhide --rm " + info.packageName);
mHideList.remove(info.packageName); mHideList.remove(info.packageName);
} }
}); });
@@ -135,8 +127,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
mList = new ArrayList<>(); mList = new ArrayList<>();
String filter = constraint.toString().toLowerCase(); String filter = constraint.toString().toLowerCase();
for (ApplicationInfo info : mOriginalList) { for (ApplicationInfo info : mOriginalList) {
if (Utils.lowercaseContains(info.loadLabel(pm), filter) if (lowercaseContains(info.loadLabel(pm), filter)
|| Utils.lowercaseContains(info.packageName, filter)) { || lowercaseContains(info.packageName, filter)) {
mList.add(info); mList.add(info);
} }
} }
@@ -157,13 +149,13 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
mOriginalList = pm.getInstalledApplications(0); mOriginalList = pm.getInstalledApplications(0);
for (Iterator<ApplicationInfo> i = mOriginalList.iterator(); i.hasNext(); ) { for (Iterator<ApplicationInfo> i = mOriginalList.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next(); ApplicationInfo info = i.next();
if (ApplicationAdapter.BLACKLIST.contains(info.packageName) || !info.enabled) { if (Const.SN_BLACKLIST.contains(info.packageName) || !info.enabled) {
i.remove(); i.remove();
} }
} }
Collections.sort(mOriginalList, (a, b) -> a.loadLabel(pm).toString().toLowerCase() Collections.sort(mOriginalList, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
.compareTo(b.loadLabel(pm).toString().toLowerCase())); .compareTo(b.loadLabel(pm).toString().toLowerCase()));
mHideList = Utils.listMagiskHide(shell); mHideList = Shell.su("magiskhide --ls");
return null; return null;
} }

View File

@@ -13,7 +13,7 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.SnackbarMaker; import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.module.Module; import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import java.util.List; import java.util.List;
@@ -38,7 +38,6 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override @Override
public void onBindViewHolder(final ViewHolder holder, int position) { public void onBindViewHolder(final ViewHolder holder, int position) {
Context context = holder.itemView.getContext(); Context context = holder.itemView.getContext();
Shell rootShell = Shell.getShell(context);
final Module module = mList.get(position); final Module module = mList.get(position);
String version = module.getVersion(); String version = module.getVersion();
@@ -56,10 +55,10 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> { holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
int snack; int snack;
if (isChecked) { if (isChecked) {
module.removeDisableFile(rootShell); module.removeDisableFile();
snack = R.string.disable_file_removed; snack = R.string.disable_file_removed;
} else { } else {
module.createDisableFile(rootShell); module.createDisableFile();
snack = R.string.disable_file_created; snack = R.string.disable_file_created;
} }
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
@@ -69,10 +68,10 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
boolean removed = module.willBeRemoved(); boolean removed = module.willBeRemoved();
int snack; int snack;
if (removed) { if (removed) {
module.deleteRemoveFile(rootShell); module.deleteRemoveFile();
snack = R.string.remove_file_deleted; snack = R.string.remove_file_deleted;
} else { } else {
module.createRemoveFile(rootShell); module.createRemoveFile();
snack = R.string.remove_file_created; snack = R.string.remove_file_created;
} }
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();

View File

@@ -15,8 +15,8 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.AlertDialogBuilder; import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.ExpandableView; import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.SnackbarMaker; import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.database.SuDatabaseHelper; import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.superuser.Policy;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;

View File

@@ -17,9 +17,9 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MarkDownWindow; import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.asyncs.ProcessRepoZip; import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
import com.topjohnwu.magisk.components.AlertDialogBuilder; 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.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
@@ -44,6 +44,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
repoDB = db; repoDB = db;
moduleMap = map; moduleMap = map;
repoPairs = new ArrayList<>(); repoPairs = new ArrayList<>();
notifyDBChanged();
} }

View File

@@ -12,8 +12,8 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.ExpandableView; import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.database.SuDatabaseHelper; import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.superuser.SuLogEntry;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;

View File

@@ -0,0 +1,83 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Proxy;
import java.net.HttpURLConnection;
import dalvik.system.DexClassLoader;
public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
private File dexPath;
private DexClassLoader loader;
public CheckSafetyNet(Activity activity) {
super(activity);
dexPath = new File(activity.getCacheDir().getParent() + "/snet", "snet.apk");
}
@Override
protected void onPreExecute() {
MagiskManager mm = MagiskManager.get();
if (mm.snet_version != Const.Value.SNET_VER) {
Shell.sh("rm -rf " + dexPath.getParent());
}
mm.snet_version = Const.Value.SNET_VER;
mm.prefs.edit().putInt(Const.Key.SNET_VER, Const.Value.SNET_VER).apply();
}
@Override
protected Exception doInBackground(Void... voids) {
try {
if (!dexPath.exists()) {
HttpURLConnection conn = WebService.request(Const.Url.SNET_URL, null);
dexPath.getParentFile().mkdir();
try (
OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
InputStream in = new BufferedInputStream(conn.getInputStream())) {
Utils.inToOut(in, out);
}
conn.disconnect();
}
loader = new DexClassLoader(dexPath.toString(), dexPath.getParent(),
null, ClassLoader.getSystemClassLoader());
} catch (Exception e) {
return e;
}
return null;
}
@Override
protected void onPostExecute(Exception err) {
MagiskManager mm = MagiskManager.get();
try {
if (err != null) throw err;
Class<?> helperClazz = loader.loadClass(Const.SNET_PKG + ".SafetyNetHelper");
Class<?> callbackClazz = loader.loadClass(Const.SNET_PKG + ".SafetyNetCallback");
Object helper = helperClazz.getConstructors()[0].newInstance(
getActivity(), dexPath.getPath(), Proxy.newProxyInstance(
loader, new Class[] { callbackClazz }, (proxy, method, args) -> {
mm.safetyNetDone.publish(false, args[0]);
return null;
}));
helperClazz.getMethod("attest").invoke(helper);
} catch (Exception e) {
e.printStackTrace();
mm.safetyNetDone.publish(false, -1);
}
super.onPostExecute(err);
}
}

View File

@@ -1,10 +1,9 @@
package com.topjohnwu.magisk.asyncs; package com.topjohnwu.magisk.asyncs;
import android.content.Context;
import com.topjohnwu.magisk.BuildConfig; import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.ShowUI;
import com.topjohnwu.magisk.utils.WebService; import com.topjohnwu.magisk.utils.WebService;
import org.json.JSONException; import org.json.JSONException;
@@ -12,37 +11,30 @@ import org.json.JSONObject;
public class CheckUpdates extends ParallelTask<Void, Void, Void> { public class CheckUpdates extends ParallelTask<Void, Void, Void> {
public static final int STABLE_CHANNEL = 0; private boolean showNotification;
public static final int BETA_CHANNEL = 1;
private static final String STABLE_URL = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/update/stable.json"; public CheckUpdates() {
private static final String BETA_URL = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/update/beta.json"; this(false);
private boolean showNotification = false;
public CheckUpdates(Context context) {
super(context);
} }
public CheckUpdates(Context context, boolean b) { public CheckUpdates(boolean b) {
super(context);
showNotification = b; showNotification = b;
} }
@Override @Override
protected Void doInBackground(Void... voids) { protected Void doInBackground(Void... voids) {
MagiskManager mm = getMagiskManager(); MagiskManager mm = MagiskManager.get();
if (mm == null) return null; String jsonStr = "";
String jsonStr;
switch (mm.updateChannel) { switch (mm.updateChannel) {
case STABLE_CHANNEL: case Const.Value.STABLE_CHANNEL:
jsonStr = WebService.getString(STABLE_URL); jsonStr = WebService.getString(Const.Url.STABLE_URL);
break; break;
case BETA_CHANNEL: case Const.Value.BETA_CHANNEL:
jsonStr = WebService.getString(BETA_URL); jsonStr = WebService.getString(Const.Url.BETA_URL);
break;
case Const.Value.CUSTOM_CHANNEL:
jsonStr = WebService.getString(mm.customChannelUrl);
break; break;
default:
jsonStr = null;
} }
try { try {
JSONObject json = new JSONObject(jsonStr); JSONObject json = new JSONObject(jsonStr);
@@ -61,13 +53,12 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
@Override @Override
protected void onPostExecute(Void v) { protected void onPostExecute(Void v) {
MagiskManager mm = getMagiskManager(); MagiskManager mm = MagiskManager.get();
if (mm == null) return;
if (showNotification && mm.updateNotification) { if (showNotification && mm.updateNotification) {
if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) { if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) {
Utils.showManagerUpdateNotification(mm); ShowUI.managerUpdateNotification();
} else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) { } else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) {
Utils.showMagiskUpdateNotification(mm); ShowUI.magiskUpdateNotification();
} }
} }
mm.updateCheckDone.publish(); mm.updateCheckDone.publish();

View File

@@ -1,65 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.content.Context;
import android.os.Build;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class DownloadBusybox extends ParallelTask<Void, Void, Void> {
private static final String BUSYBOX_ARM = "https://github.com/topjohnwu/ndk-busybox/releases/download/1.27.2/busybox-arm";
private static final String BUSYBOX_X86 = "https://github.com/topjohnwu/ndk-busybox/releases/download/1.27.2/busybox-x86";
private File busybox;
public DownloadBusybox(Context context) {
super(context);
busybox = new File(context.getCacheDir(), "busybox");
}
@Override
protected Void doInBackground(Void... voids) {
Context context = getMagiskManager();
Utils.removeItem(getShell(), context.getApplicationInfo().dataDir + "/busybox");
try {
FileOutputStream out = new FileOutputStream(busybox);
InputStream in = WebService.request(WebService.GET,
Build.SUPPORTED_32_BIT_ABIS[0].contains("x86") ?
BUSYBOX_X86 :
BUSYBOX_ARM,
null
);
if (in == null) throw new IOException();
BufferedInputStream bis = new BufferedInputStream(in);
byte[] buffer = new byte[4096];
int len;
while ((len = bis.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
if (busybox.exists()) {
getShell().su_raw(
"rm -rf " + MagiskManager.BUSYBOXPATH,
"mkdir -p " + MagiskManager.BUSYBOXPATH,
"cp " + busybox + " " + MagiskManager.BUSYBOXPATH,
"chmod -R 755 " + MagiskManager.BUSYBOXPATH,
MagiskManager.BUSYBOXPATH + "/busybox --install -s " + MagiskManager.BUSYBOXPATH
);
busybox.delete();
}
return null;
}
}

View File

@@ -3,13 +3,17 @@ package com.topjohnwu.magisk.asyncs;
import android.app.Activity; import android.app.Activity;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.AdaptiveList; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils; import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@@ -21,101 +25,84 @@ import java.util.List;
public class FlashZip extends ParallelTask<Void, Void, Integer> { public class FlashZip extends ParallelTask<Void, Void, Integer> {
private Uri mUri; private Uri mUri;
private File mCachedFile, mScriptFile, mCheckFile; private File mCachedFile;
private List<String> console, logs;
private String mFilename; public FlashZip(Activity context, Uri uri, List<String> console, List<String> logs) {
private AdaptiveList<String> mList;
public FlashZip(Activity context, Uri uri, AdaptiveList<String> list) {
super(context); super(context);
mUri = uri; mUri = uri;
mList = list; this.console = console;
this.logs = logs;
mCachedFile = new File(context.getCacheDir(), "install.zip"); mCachedFile = new File(context.getCacheDir(), "install.zip");
mScriptFile = new File(context.getCacheDir(), "/META-INF/com/google/android/update-binary");
mCheckFile = new File(mScriptFile.getParent(), "updater-script");
// Try to get the filename ourselves
mFilename = Utils.getNameFromUri(context, mUri);
} }
private boolean unzipAndCheck() throws Exception { private boolean unzipAndCheck() throws Exception {
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android", false); ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android", true);
List<String> ret = Utils.readFile(getShell(), mCheckFile.getPath()); List<String> ret = Utils.readFile(new File(mCachedFile.getParentFile(), "updater-script").getPath());
return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK"); return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
} }
@Override
protected void onPreExecute() {
// UI updates must run in the UI thread
mList.setCallback(this::publishProgress);
}
@Override
protected void onProgressUpdate(Void... values) {
mList.updateView();
}
@Override @Override
protected Integer doInBackground(Void... voids) { protected Integer doInBackground(Void... voids) {
MagiskManager mm = getMagiskManager(); MagiskManager mm = MagiskManager.get();
if (mm == null) return -1;
try { try {
mList.add("- Copying zip to temp directory"); console.add("- Copying zip to temp directory");
mCachedFile.delete(); mCachedFile.delete();
try ( try (
InputStream in = mm.getContentResolver().openInputStream(mUri); InputStream in = mm.getContentResolver().openInputStream(mUri);
OutputStream out = new FileOutputStream(mCachedFile) OutputStream out = new BufferedOutputStream(new FileOutputStream(mCachedFile))
) { ) {
if (in == null) throw new FileNotFoundException(); if (in == null) throw new FileNotFoundException();
byte buffer[] = new byte[1024]; InputStream buf= new BufferedInputStream(in);
int length; Utils.inToOut(buf, out);
while ((length = in.read(buffer)) > 0)
out.write(buffer, 0, length);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
mList.add("! Invalid Uri"); console.add("! Invalid Uri");
throw e; throw e;
} catch (IOException e) { } catch (IOException e) {
mList.add("! Cannot copy to cache"); console.add("! Cannot copy to cache");
throw e; throw e;
} }
if (!unzipAndCheck()) return 0; if (!unzipAndCheck()) return 0;
mList.add("- Installing " + mFilename); console.add("- Installing " + Utils.getNameFromUri(mm, mUri));
getShell().su(mList, Shell.getShell().run(console, logs,
"BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile + "cd " + mCachedFile.getParent(),
" && echo 'Success!' || echo 'Failed!'" "BOOTMODE=true sh update-binary dummy 1 " + mCachedFile + " || echo 'Failed!'"
); );
if (TextUtils.equals(mList.get(mList.size() - 1), "Success!"))
return 1; if (TextUtils.equals(console.get(console.size() - 1), "Failed!"))
return -1;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return -1;
} }
return -1; console.add("- All done!");
return 1;
} }
// -1 = error, manual install; 0 = invalid zip; 1 = success // -1 = error, manual install; 0 = invalid zip; 1 = success
@Override @Override
protected void onPostExecute(Integer result) { protected void onPostExecute(Integer result) {
MagiskManager mm = getMagiskManager(); FlashActivity activity = (FlashActivity) getActivity();
if (mm == null) return; Shell.su_raw(
getShell().su_raw(
"rm -rf " + mCachedFile.getParent(), "rm -rf " + mCachedFile.getParent(),
"rm -rf " + MagiskManager.TMP_FOLDER_PATH "rm -rf " + Const.TMP_FOLDER_PATH
); );
switch (result) { switch (result) {
case -1: case -1:
mList.add(mm.getString(R.string.install_error)); console.add("! Installation failed");
Utils.showUriSnack(getActivity(), mUri); Utils.showUriSnack(getActivity(), mUri);
break; break;
case 0: case 0:
mList.add(mm.getString(R.string.invalid_zip)); console.add("! This zip is not a Magisk Module!");
break; break;
case 1: case 1:
// Success // Success
new LoadModules(mm).exec(); new LoadModules().exec();
break; break;
} }
super.onPostExecute(result); activity.reboot.setVisibility(result > 0 ? View.VISIBLE : View.GONE);
activity.buttonPanel.setVisibility(View.VISIBLE);
} }
} }

View File

@@ -1,73 +1,145 @@
package com.topjohnwu.magisk.asyncs; package com.topjohnwu.magisk.asyncs;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.widget.Toast; import android.widget.Toast;
import com.topjohnwu.crypto.JarMap;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.superuser.Policy; import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils; import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.security.SecureRandom;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.jar.JarEntry;
public class HideManager extends ParallelTask<Void, Void, Boolean> { public class HideManager extends ParallelTask<Void, Void, Boolean> {
public HideManager(Context context) { private String genPackageName(String prefix, int length) {
super(context); StringBuilder builder = new StringBuilder(length);
builder.append(prefix);
length -= prefix.length();
SecureRandom random = new SecureRandom();
String base = "abcdefghijklmnopqrstuvwxyz";
String alpha = base + base.toUpperCase();
String full = alpha + "0123456789..........";
char next, prev = '\0';
for (int i = 0; i < length; ++i) {
if (prev == '.' || i == length - 1 || i == 0) {
next = alpha.charAt(random.nextInt(alpha.length()));
} else {
next = full.charAt(random.nextInt(full.length()));
}
builder.append(next);
prev = next;
}
return builder.toString();
}
private int findOffset(byte buf[], byte pattern[]) {
int offset = -1;
for (int i = 0; i < buf.length - pattern.length; ++i) {
boolean match = true;
for (int j = 0; j < pattern.length; ++j) {
if (buf[i + j] != pattern[j]) {
match = false;
break;
}
}
if (match) {
offset = i;
break;
}
}
return offset;
}
/* It seems that AAPT sometimes generate another type of string format */
private boolean fallbackPatch(byte xml[], String from, String to) {
byte[] target = new byte[from.length() * 2 + 2];
for (int i = 0; i < from.length(); ++i) {
target[i * 2] = (byte) from.charAt(i);
}
int offset = findOffset(xml, target);
if (offset < 0)
return false;
byte[] dest = new byte[target.length - 2];
for (int i = 0; i < to.length(); ++i) {
dest[i * 2] = (byte) to.charAt(i);
}
System.arraycopy(dest, 0, xml, offset, dest.length);
return true;
}
private boolean findAndPatch(byte xml[], String from, String to) {
byte target[] = (from + '\0').getBytes();
int offset = findOffset(xml, target);
if (offset < 0)
return fallbackPatch(xml, from, to);
System.arraycopy(to.getBytes(), 0, xml, offset, to.length());
return true;
} }
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
getMagiskManager().toast(R.string.hide_manager_toast, Toast.LENGTH_SHORT); MagiskManager.toast(R.string.hide_manager_toast, Toast.LENGTH_SHORT);
MagiskManager.toast(R.string.hide_manager_toast2, Toast.LENGTH_LONG);
} }
@Override @Override
protected Boolean doInBackground(Void... voids) { protected Boolean doInBackground(Void... voids) {
MagiskManager mm = getMagiskManager(); MagiskManager mm = MagiskManager.get();
if (mm == null)
return false;
// Generate a new unhide app with random package name // Generate a new unhide app with random package name
File unhideAPK = new File(Environment.getExternalStorageDirectory() + "/MagiskManager", "unhide.apk"); File repack = new File(Const.EXTERNAL_PATH, "repack.apk");
unhideAPK.getParentFile().mkdirs(); repack.getParentFile().mkdirs();
String pkg = ZipUtils.generateUnhide(mm, unhideAPK); String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length());
// Install the application
List<String> ret = getShell().su("pm install " + unhideAPK + ">/dev/null && echo true || echo false");
unhideAPK.delete();
if (!Utils.isValidShellResponse(ret) || !Boolean.parseBoolean(ret.get(0)))
return false;
try { try {
// Allow the application to gain root by default // Read whole APK into memory
PackageManager pm = mm.getPackageManager(); JarMap apk = new JarMap(new FileInputStream(mm.getPackageCodePath()));
int uid = pm.getApplicationInfo(pkg, 0).uid; JarEntry je = new JarEntry(Const.ANDROID_MANIFEST);
Policy policy = new Policy(uid, pm); byte xml[] = apk.getRawData(je);
policy.policy = Policy.ALLOW;
policy.notification = false; if (!findAndPatch(xml, Const.ORIG_PKG_NAME, pkg))
policy.logging = false; return false;
mm.suDB.addPolicy(policy); if (!findAndPatch(xml, Const.ORIG_PKG_NAME + ".provider", pkg + ".provider"))
} catch (PackageManager.NameNotFoundException e) { return false;
// Write in changes
apk.getOutputStream(je).write(xml);
// Sign the APK
ZipUtils.signZip(apk, repack, false);
} catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return false; return false;
} }
// Hide myself! // Install the application
getShell().su_raw("pm hide " + mm.getPackageName());
List<String> ret = Shell.su(String.format(Locale.US,
"pm install --user %d %s >/dev/null && echo true || echo false",
mm.userId, repack));
repack.delete();
if (!Utils.isValidShellResponse(ret) || !Boolean.parseBoolean(ret.get(0)))
return false;
mm.suDB.setStrings(Const.Key.SU_REQUESTER, pkg);
Shell.su_raw(String.format(Locale.US, "pm uninstall --user %d %s", mm.userId, mm.getPackageName()));
return true; return true;
} }
@Override @Override
protected void onPostExecute(Boolean b) { protected void onPostExecute(Boolean b) {
MagiskManager mm = getMagiskManager();
if (mm == null)
return;
if (!b) { if (!b) {
mm.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG); MagiskManager.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
} }
super.onPostExecute(b); super.onPostExecute(b);
} }

View File

@@ -1,45 +1,36 @@
package com.topjohnwu.magisk.asyncs; package com.topjohnwu.magisk.asyncs;
import android.content.Context;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.module.BaseModule; import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.module.Module; import com.topjohnwu.magisk.container.ValueSortedMap;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.ValueSortedMap;
import java.util.List;
public class LoadModules extends ParallelTask<Void, Void, Void> { public class LoadModules extends ParallelTask<Void, Void, Void> {
public LoadModules(Context context) { private List<String> getModList() {
super(context); String command = "ls -d " + Const.MAGISK_PATH() + "/* | grep -v lost+found";
return Shell.su(command);
} }
@Override @Override
protected Void doInBackground(Void... voids) { protected Void doInBackground(Void... voids) {
MagiskManager mm = getMagiskManager(); MagiskManager mm = MagiskManager.get();
if (mm == null) return null;
Logger.dev("LoadModules: Loading modules");
mm.moduleMap = new ValueSortedMap<>(); mm.moduleMap = new ValueSortedMap<>();
for (String path : Utils.getModList(getShell(), MagiskManager.MAGISK_PATH)) { for (String path : getModList()) {
Logger.dev("LoadModules: Adding modules from " + path); Module module = new Module(path);
try { mm.moduleMap.put(module.getId(), module);
Module module = new Module(getShell(), path);
mm.moduleMap.put(module.getId(), module);
} catch (BaseModule.CacheModException ignored) {}
} }
Logger.dev("LoadModules: Data load done");
return null; return null;
} }
@Override @Override
protected void onPostExecute(Void v) { protected void onPostExecute(Void v) {
MagiskManager mm = getMagiskManager(); MagiskManager.get().moduleLoadDone.publish();
if (mm == null) return;
mm.moduleLoadDone.publish();
super.onPostExecute(v); super.onPostExecute(v);
} }
} }

View File

@@ -4,6 +4,7 @@ import android.app.Activity;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.webkit.WebView; import android.webkit.WebView;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.WebService; import com.topjohnwu.magisk.utils.WebService;
@@ -29,7 +30,7 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
Node doc = parser.parse(md); Node doc = parser.parse(md);
return String.format( return String.format(
"<link rel='stylesheet' type='text/css' href='file:///android_asset/%s.css'/> %s", "<link rel='stylesheet' type='text/css' href='file:///android_asset/%s.css'/> %s",
getMagiskManager().isDarkTheme ? "dark" : "light", renderer.render(doc)); MagiskManager.get().isDarkTheme ? "dark" : "light", renderer.render(doc));
} }
@Override @Override

View File

@@ -1,30 +1,19 @@
package com.topjohnwu.magisk.asyncs; package com.topjohnwu.magisk.asyncs;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private WeakReference<Activity> weakActivity; private WeakReference<Activity> weakActivity;
private WeakReference<MagiskManager> weakMagiskManager;
private Runnable callback = null; private Runnable callback = null;
public ParallelTask() {} public ParallelTask() {}
public ParallelTask(Context context) {
weakMagiskManager = new WeakReference<>(Utils.getMagiskManager(context));
}
public ParallelTask(Activity context) { public ParallelTask(Activity context) {
this((Context) context);
weakActivity = new WeakReference<>(context); weakActivity = new WeakReference<>(context);
} }
@@ -32,15 +21,6 @@ public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<P
return weakActivity.get(); return weakActivity.get();
} }
protected MagiskManager getMagiskManager() {
return weakMagiskManager.get();
}
protected Shell getShell() {
MagiskManager magiskManager = getMagiskManager();
return magiskManager == null ? null : Shell.getShell(magiskManager);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public ParallelTask<Params, Progress, Result> exec(Params... params) { public ParallelTask<Params, Progress, Result> exec(Params... params) {
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);

View File

@@ -5,12 +5,15 @@ import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Handler;
import android.support.annotation.NonNull;
import android.widget.Toast; import android.widget.Toast;
import com.topjohnwu.magisk.FlashActivity; import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.container.InputStreamWrapper;
import com.topjohnwu.magisk.utils.Const;
import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService; import com.topjohnwu.magisk.utils.WebService;
@@ -21,34 +24,60 @@ import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> { public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
private ProgressDialog progressDialog; private ProgressDialog progressDialog;
private boolean mInstall; private boolean mInstall;
private String mLink, mFile; private String mLink;
private File mFile;
private int progress = 0, total = -1;
private Handler mHandler;
public ProcessRepoZip(Activity context, String link, String filename, boolean install) { public ProcessRepoZip(Activity context, String link, String filename, boolean install) {
super(context); super(context);
mLink = link; mLink = link;
mFile = Environment.getExternalStorageDirectory() + "/MagiskManager/" + filename; mFile = new File(Const.EXTERNAL_PATH, filename);
mInstall = install; mInstall = install;
mHandler = new Handler();
}
private void removeTopFolder(File input, File output) throws IOException {
JarEntry entry;
try (
JarInputStream in = new JarInputStream(new BufferedInputStream(new FileInputStream(input)));
JarOutputStream out = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(output)))
) {
String path;
while ((entry = in.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.equals("system/placeholder")) {
continue;
}
out.putNextEntry(new JarEntry(path));
Utils.inToOut(in, out);
}
}
} }
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
Activity activity = getActivity(); Activity activity = getActivity();
progressDialog = ProgressDialog.show(activity, mFile.getParentFile().mkdirs();
activity.getString(R.string.zip_download_title), progressDialog = ProgressDialog.show(activity, activity.getString(R.string.zip_download_title), activity.getString(R.string.zip_download_msg, 0));
activity.getString(R.string.zip_download_msg));
}
@Override
protected void onProgressUpdate(Void... values) {
progressDialog.setTitle(R.string.zip_process_title);
progressDialog.setMessage(getActivity().getString(R.string.zip_process_msg));
} }
@Override @Override
@@ -56,40 +85,49 @@ public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
Activity activity = getActivity(); Activity activity = getActivity();
if (activity == null) return null; if (activity == null) return null;
try { try {
// Request zip from Internet // Request zip from Internet
InputStream in = WebService.request(WebService.GET, mLink, null); HttpURLConnection conn;
if (in == null) return false; do {
in = new BufferedInputStream(in); conn = WebService.request(mLink, null);
if (conn == null) return null;
total = conn.getContentLength();
if (total < 0)
conn.disconnect();
else
break;
} while (true);
// Temp files // Temp files
File temp1 = new File(activity.getCacheDir(), "1.zip"); File temp1 = new File(activity.getCacheDir(), "1.zip");
File temp2 = new File(temp1.getParentFile(), "2.zip"); File temp2 = new File(temp1.getParentFile(), "2.zip");
temp1.getParentFile().mkdir(); temp1.getParentFile().mkdir();
// First remove top folder in Github source zip, Web -> temp1 // First download the zip, Web -> temp1
ZipUtils.removeTopFolder(in, temp1); try (
InputStream in = new BufferedInputStream(new ProgressInputStream(conn.getInputStream()));
publishProgress(); OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))
// Then sign the zip for the first time, temp1 -> temp2
ZipUtils.signZip(activity, temp1, temp2, false);
// Adjust the zip to prevent unzip issues, temp2 -> temp1
ZipUtils.zipAdjust(temp2.getPath(), temp1.getPath());
// Finally, sign the whole zip file again, temp1 -> temp2
ZipUtils.signZip(activity, temp1, temp2, true);
// Write it to the target zip, temp2 -> file
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(mFile));
InputStream source = new BufferedInputStream(new FileInputStream(temp2))
) { ) {
byte[] buffer = new byte[4096]; Utils.inToOut(in, out);
int length; in.close();
while ((length = source.read(buffer)) > 0)
out.write(buffer, 0, length);
} }
conn.disconnect();
mHandler.post(() -> {
progressDialog.setTitle(R.string.zip_process_title);
progressDialog.setMessage(getActivity().getString(R.string.zip_process_msg));
});
// First remove top folder in Github source zip, temp1 -> temp2
removeTopFolder(temp1, temp2);
// Then sign the zip for the first time, temp2 -> temp1
ZipUtils.signZip(temp2, temp1, false);
// Adjust the zip to prevent unzip issues, temp1 -> temp2
ZipUtils.zipAdjust(temp1.getPath(), temp2.getPath());
// Finally, sign the whole zip file again, temp2 -> target
ZipUtils.signZip(temp2, mFile, true);
// Delete temp files // Delete temp files
temp1.delete(); temp1.delete();
@@ -97,7 +135,6 @@ public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
return true; return true;
} catch (Exception e) { } catch (Exception e) {
Logger.error("ProcessRepoZip: Error!");
e.printStackTrace(); e.printStackTrace();
return false; return false;
} }
@@ -108,24 +145,60 @@ public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
Activity activity = getActivity(); Activity activity = getActivity();
if (activity == null) return; if (activity == null) return;
progressDialog.dismiss(); progressDialog.dismiss();
Uri uri = Uri.fromFile(new File(mFile));
if (result) { if (result) {
Uri uri = Uri.fromFile(mFile);
if (Shell.rootAccess() && mInstall) { if (Shell.rootAccess() && mInstall) {
Intent intent = new Intent(getActivity(), FlashActivity.class); Intent intent = new Intent(activity, FlashActivity.class);
intent.setData(uri).putExtra(FlashActivity.SET_ACTION, FlashActivity.FLASH_ZIP); intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
activity.startActivity(intent); activity.startActivity(intent);
} else { } else {
Utils.showUriSnack(activity, uri); Utils.showUriSnack(activity, uri);
} }
} else { } else {
Utils.getMagiskManager(activity).toast(R.string.process_error, Toast.LENGTH_LONG); MagiskManager.toast(R.string.process_error, Toast.LENGTH_LONG);
} }
super.onPostExecute(result); super.onPostExecute(result);
} }
@Override @Override
public ParallelTask<Void, Void, Boolean> exec(Void... voids) { public ParallelTask<Void, Object, Boolean> exec(Void... voids) {
Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> super.exec(voids)); Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE,
() -> super.exec(voids));
return this; return this;
} }
private class ProgressInputStream extends InputStreamWrapper {
ProgressInputStream(InputStream in) {
super(in);
}
private void updateDlProgress(int step) {
progress += step;
progressDialog.setMessage(getActivity().getString(R.string.zip_download_msg, (int) (100 * (double) progress / total + 0.5)));
}
@Override
public synchronized int read() throws IOException {
int b = super.read();
if (b > 0) {
mHandler.post(() -> updateDlProgress(1));
}
return b;
}
@Override
public int read(@NonNull byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public synchronized int read(@NonNull byte[] b, int off, int len) throws IOException {
int read = super.read(b, off, len);
if (read > 0) {
mHandler.post(() -> updateDlProgress(read));
}
return read;
}
}
} }

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