mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-11-03 15:52:30 +01:00 
			
		
		
		
	Compare commits
	
		
			172 Commits
		
	
	
		
			manager-v4
			...
			manager-v5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					55ecc41d06 | ||
| 
						 | 
					28fcdf2cbb | ||
| 
						 | 
					24087679a8 | ||
| 
						 | 
					5ac6a8cb4a | ||
| 
						 | 
					668d85d14e | ||
| 
						 | 
					c11a3dc95c | ||
| 
						 | 
					56f57c20a2 | ||
| 
						 | 
					240d14779a | ||
| 
						 | 
					3550d1e61c | ||
| 
						 | 
					6513ad249c | ||
| 
						 | 
					50297b1880 | ||
| 
						 | 
					f189b78b9e | ||
| 
						 | 
					5c0250f495 | ||
| 
						 | 
					2093f726e9 | ||
| 
						 | 
					10efe3859d | ||
| 
						 | 
					6933bcf7bb | ||
| 
						 | 
					2ea046cd80 | ||
| 
						 | 
					f4097a372b | ||
| 
						 | 
					87ea2a2bef | ||
| 
						 | 
					cc14a1c361 | ||
| 
						 | 
					bcdface60d | ||
| 
						 | 
					4dc9419d2e | ||
| 
						 | 
					d2bcac813e | ||
| 
						 | 
					080c37a7f6 | ||
| 
						 | 
					f9a3838db6 | ||
| 
						 | 
					1e61db104b | ||
| 
						 | 
					30a9c7718d | ||
| 
						 | 
					34b052b5d3 | ||
| 
						 | 
					aaa12853ad | ||
| 
						 | 
					b0ab55b0bf | ||
| 
						 | 
					d2f8496f4e | ||
| 
						 | 
					1a69b16d36 | ||
| 
						 | 
					b5e8673e62 | ||
| 
						 | 
					264c6a50b6 | ||
| 
						 | 
					493642eb38 | ||
| 
						 | 
					28d42b9164 | ||
| 
						 | 
					42f29062ca | ||
| 
						 | 
					c4377ed6c2 | ||
| 
						 | 
					7d283ed65f | ||
| 
						 | 
					bf1f941e50 | ||
| 
						 | 
					789fef34ba | ||
| 
						 | 
					1daf5a611c | ||
| 
						 | 
					6aed1db67e | ||
| 
						 | 
					cf68854770 | ||
| 
						 | 
					711392c73b | ||
| 
						 | 
					9573c32481 | ||
| 
						 | 
					a15f80f79d | ||
| 
						 | 
					23e7475f06 | ||
| 
						 | 
					1eb571b787 | ||
| 
						 | 
					dd3b716d85 | ||
| 
						 | 
					28649c07e3 | ||
| 
						 | 
					961e02be0d | ||
| 
						 | 
					a161491bfd | ||
| 
						 | 
					e0b4d1c1e4 | ||
| 
						 | 
					fd4aaab137 | ||
| 
						 | 
					42d14d5ca2 | ||
| 
						 | 
					d3ff482c9b | ||
| 
						 | 
					f682368eeb | ||
| 
						 | 
					4a5d033efb | ||
| 
						 | 
					343161b195 | ||
| 
						 | 
					bc576a9659 | ||
| 
						 | 
					19e407fcc4 | ||
| 
						 | 
					bc7327d004 | ||
| 
						 | 
					666fa1c797 | ||
| 
						 | 
					0eda4a7821 | ||
| 
						 | 
					862058fd2b | ||
| 
						 | 
					69e5bcd57d | ||
| 
						 | 
					efeddda328 | ||
| 
						 | 
					ff6938280e | ||
| 
						 | 
					1e4425b30f | ||
| 
						 | 
					b5d1d8cdad | ||
| 
						 | 
					029be5ccca | ||
| 
						 | 
					29c2d785b5 | ||
| 
						 | 
					abda8cfa32 | ||
| 
						 | 
					44e7d79d4c | ||
| 
						 | 
					9a1dc8ee0e | ||
| 
						 | 
					27879c3f01 | ||
| 
						 | 
					29096eb5d7 | ||
| 
						 | 
					a573baea03 | ||
| 
						 | 
					5af07c4531 | ||
| 
						 | 
					44e36feb09 | ||
| 
						 | 
					2a7d996881 | ||
| 
						 | 
					738f943a68 | ||
| 
						 | 
					47e62a5681 | ||
| 
						 | 
					1ecbfd7590 | ||
| 
						 | 
					67c139a04b | ||
| 
						 | 
					31cc008249 | ||
| 
						 | 
					9cb026439d | ||
| 
						 | 
					e6f10176c6 | ||
| 
						 | 
					0917c79470 | ||
| 
						 | 
					597baa986d | ||
| 
						 | 
					75cc4b4843 | ||
| 
						 | 
					aac088d496 | ||
| 
						 | 
					a822e5bbc5 | ||
| 
						 | 
					c527249c21 | ||
| 
						 | 
					9ef798f534 | ||
| 
						 | 
					e69b99f089 | ||
| 
						 | 
					55b8079e86 | ||
| 
						 | 
					e272dbe9af | ||
| 
						 | 
					962f8354ac | ||
| 
						 | 
					20e4a960f7 | ||
| 
						 | 
					82249cb50a | ||
| 
						 | 
					fad417e553 | ||
| 
						 | 
					5ba692f50c | ||
| 
						 | 
					907e01e524 | ||
| 
						 | 
					b8ed23efa7 | ||
| 
						 | 
					2b3bbf7e67 | ||
| 
						 | 
					464fe627a3 | ||
| 
						 | 
					6a9e39c470 | ||
| 
						 | 
					7fec9a3cc6 | ||
| 
						 | 
					008f6ef462 | ||
| 
						 | 
					2440c108ca | ||
| 
						 | 
					430baad8a4 | ||
| 
						 | 
					51132e74b4 | ||
| 
						 | 
					a4f33e106a | ||
| 
						 | 
					baba3190e0 | ||
| 
						 | 
					47b13aa5ea | ||
| 
						 | 
					ae88d3054d | ||
| 
						 | 
					411b600e14 | ||
| 
						 | 
					0a0ad9a184 | ||
| 
						 | 
					234bead59e | ||
| 
						 | 
					76de310986 | ||
| 
						 | 
					817f050bcd | ||
| 
						 | 
					60ae685d1e | ||
| 
						 | 
					4c7bdbb284 | ||
| 
						 | 
					435251ca41 | ||
| 
						 | 
					324a0dd38f | ||
| 
						 | 
					cc77d93918 | ||
| 
						 | 
					0ea7d8bd8c | ||
| 
						 | 
					849b217143 | ||
| 
						 | 
					9af6efba59 | ||
| 
						 | 
					079d6f06ef | ||
| 
						 | 
					9cf0757689 | ||
| 
						 | 
					b54c438948 | ||
| 
						 | 
					c3ff4bfdad | ||
| 
						 | 
					5d62e066e2 | ||
| 
						 | 
					e94219c5a3 | ||
| 
						 | 
					8ed9634adf | ||
| 
						 | 
					0aefa9599f | ||
| 
						 | 
					e279cf0575 | ||
| 
						 | 
					a3f0ef8e77 | ||
| 
						 | 
					8eba05ed4a | ||
| 
						 | 
					2f78155723 | ||
| 
						 | 
					6785221479 | ||
| 
						 | 
					9bc410dd3d | ||
| 
						 | 
					2491ab6bf9 | ||
| 
						 | 
					f615ed40cd | ||
| 
						 | 
					430f2cafc1 | ||
| 
						 | 
					0ad049da88 | ||
| 
						 | 
					2c7691567b | ||
| 
						 | 
					1d70d0fe94 | ||
| 
						 | 
					ac44f05811 | ||
| 
						 | 
					d99252f394 | ||
| 
						 | 
					b58c7ba7c5 | ||
| 
						 | 
					8c5acd1a0a | ||
| 
						 | 
					b9b1ebf18c | ||
| 
						 | 
					8ca132cef0 | ||
| 
						 | 
					a03bb90754 | ||
| 
						 | 
					d1c939f48a | ||
| 
						 | 
					21b11f1b48 | ||
| 
						 | 
					23c84a7803 | ||
| 
						 | 
					f9ab060403 | ||
| 
						 | 
					df7a5bf149 | ||
| 
						 | 
					c4afa069df | ||
| 
						 | 
					1bfafdb44f | ||
| 
						 | 
					1ef5bd7076 | ||
| 
						 | 
					29176fa4f4 | ||
| 
						 | 
					958c95732b | ||
| 
						 | 
					44b0d4127c | ||
| 
						 | 
					1418ec2416 | ||
| 
						 | 
					b51978f51c | ||
| 
						 | 
					b07361580a | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -3,6 +3,6 @@
 | 
			
		||||
/local.properties
 | 
			
		||||
.idea/
 | 
			
		||||
/build
 | 
			
		||||
app/app-release.apk
 | 
			
		||||
app/release
 | 
			
		||||
*.hprof
 | 
			
		||||
app/.externalNativeBuild/
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							@@ -1,4 +1,6 @@
 | 
			
		||||
# Magisk Manager
 | 
			
		||||
The project should be built with Android Studio version 2.2.0+  
 | 
			
		||||
I use Java 8 features, which requires Jack compiler and it's only available in 2.2.0+  
 | 
			
		||||
Also, you need to install CMake and NDK to build the zipadjust library for zip preprocessing
 | 
			
		||||
# Magisk Manager 
 | 
			
		||||
You need to install CMake and NDK to build the zipadjust library for zip preprocessing
 | 
			
		||||
 | 
			
		||||
## Pre-built Binaries
 | 
			
		||||
Busybox (arm and x86) compiled by osm0sis (`libbusybox.so` under `app\src\main\jniLibs`)  
 | 
			
		||||
Source and more info: [osm0sis' Odds and Ends](https://forum.xda-developers.com/showthread.php?t=2239421)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +1,25 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    compileSdkVersion 25
 | 
			
		||||
    buildToolsVersion "25.0.2"
 | 
			
		||||
    compileSdkVersion 26
 | 
			
		||||
    buildToolsVersion "26.0.0"
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId "com.topjohnwu.magisk"
 | 
			
		||||
        minSdkVersion 21
 | 
			
		||||
        targetSdkVersion 25
 | 
			
		||||
        versionCode 22
 | 
			
		||||
        versionName "4.1"
 | 
			
		||||
        jackOptions {
 | 
			
		||||
            enabled true
 | 
			
		||||
            jackInProcess true
 | 
			
		||||
        }
 | 
			
		||||
        targetSdkVersion 26
 | 
			
		||||
        versionCode 51
 | 
			
		||||
        versionName "5.1.1"
 | 
			
		||||
        ndk {
 | 
			
		||||
            moduleName 'zipadjust'
 | 
			
		||||
            abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
 | 
			
		||||
            abiFilters 'x86', 'armeabi-v7a'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    buildTypes {
 | 
			
		||||
        release {
 | 
			
		||||
            minifyEnabled true
 | 
			
		||||
            shrinkResources true
 | 
			
		||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -31,35 +28,36 @@ android {
 | 
			
		||||
        targetCompatibility JavaVersion.VERSION_1_8
 | 
			
		||||
    }
 | 
			
		||||
    dexOptions {
 | 
			
		||||
        preDexLibraries = true
 | 
			
		||||
        preDexLibraries true
 | 
			
		||||
        javaMaxHeapSize "2g"
 | 
			
		||||
    }
 | 
			
		||||
    externalNativeBuild {
 | 
			
		||||
        cmake {
 | 
			
		||||
            path 'src/main/jni/CMakeLists.txt'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    lintOptions {
 | 
			
		||||
        disable 'MissingTranslation'
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
repositories {
 | 
			
		||||
    jcenter()
 | 
			
		||||
    maven { url "https://jitpack.io" }
 | 
			
		||||
    maven { url "https://maven.google.com" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    compile fileTree(include: ['*.jar'], dir: 'libs')
 | 
			
		||||
 | 
			
		||||
    compile 'com.android.support:recyclerview-v7:25.1.1'
 | 
			
		||||
    compile 'com.android.support:cardview-v7:25.1.1'
 | 
			
		||||
    compile 'com.android.support:design:25.1.1'
 | 
			
		||||
    compile 'com.android.support:support-v4:25.1.1'
 | 
			
		||||
    compile 'com.android.support:support-v13:25.1.1'
 | 
			
		||||
    compile 'com.jakewharton:butterknife:8.5.1'
 | 
			
		||||
    compile 'com.google.code.gson:gson:2.8.0'
 | 
			
		||||
    compile 'com.github.clans:fab:1.6.4'
 | 
			
		||||
    compile 'com.thoughtbot:expandablerecyclerview:1.4'
 | 
			
		||||
    compile 'com.madgag.spongycastle:core:1.54.0.0'
 | 
			
		||||
    compile 'com.madgag.spongycastle:prov:1.54.0.0'
 | 
			
		||||
    compile 'com.madgag.spongycastle:pkix:1.54.0.0'
 | 
			
		||||
    compile 'com.madgag.spongycastle:pg:1.54.0.0'
 | 
			
		||||
    compile 'com.google.android.gms:play-services-safetynet:9.0.1'
 | 
			
		||||
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
 | 
			
		||||
    implementation fileTree(include: ['*.jar'], dir: 'libs')
 | 
			
		||||
    implementation 'com.android.support:recyclerview-v7:26.0.0-beta2'
 | 
			
		||||
    implementation 'com.android.support:cardview-v7:26.0.0-beta2'
 | 
			
		||||
    implementation 'com.android.support:design:26.0.0-beta2'
 | 
			
		||||
    implementation 'com.android.support:support-v4:26.0.0-beta2'
 | 
			
		||||
    implementation 'com.jakewharton:butterknife:8.7.0'
 | 
			
		||||
    implementation 'com.thoughtbot:expandablerecyclerview:1.4'
 | 
			
		||||
    implementation 'us.feras.mdv:markdownview:1.1.0'
 | 
			
		||||
    implementation 'com.madgag.spongycastle:core:1.54.0.0'
 | 
			
		||||
    implementation 'com.madgag.spongycastle:prov:1.54.0.0'
 | 
			
		||||
    implementation 'com.madgag.spongycastle:pkix:1.54.0.0'
 | 
			
		||||
    implementation 'com.google.android.gms:play-services-safetynet:9.0.1'
 | 
			
		||||
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							@@ -16,31 +16,10 @@
 | 
			
		||||
#   public *;
 | 
			
		||||
#}
 | 
			
		||||
 | 
			
		||||
# Gson uses generic type information stored in a class file when working with fields. Proguard
 | 
			
		||||
# removes such information by default, so configure it to keep all of it.
 | 
			
		||||
-keepattributes Signature
 | 
			
		||||
 | 
			
		||||
# For using GSON @Expose annotation
 | 
			
		||||
-keepattributes *Annotation*
 | 
			
		||||
 | 
			
		||||
# Gson specific classes
 | 
			
		||||
-keep class sun.misc.Unsafe { *; }
 | 
			
		||||
-keep class com.google.gson.** { *; }
 | 
			
		||||
 | 
			
		||||
# Application classes that will be serialized/deserialized over Gson
 | 
			
		||||
-keep class com.topjohnwu.magisk.module.** { *; }
 | 
			
		||||
-keep class com.topjohnwu.magisk.module.ModuleHelper$ValueSortedMap { *; }
 | 
			
		||||
 | 
			
		||||
# Prevent proguard from stripping interface information from TypeAdapterFactory,
 | 
			
		||||
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
 | 
			
		||||
-keep class * implements com.google.gson.TypeAdapterFactory
 | 
			
		||||
-keep class * implements com.google.gson.JsonSerializer
 | 
			
		||||
-keep class * implements com.google.gson.JsonDeserializer
 | 
			
		||||
 | 
			
		||||
-keep class android.support.v7.internal.** { *; }
 | 
			
		||||
-keep interface android.support.v7.internal.** { *; }
 | 
			
		||||
-keep class android.support.v7.** { *; }
 | 
			
		||||
-keep interface android.support.v7.** { *; }
 | 
			
		||||
 | 
			
		||||
# SpongyCastle
 | 
			
		||||
-keep class org.spongycastle.** {*;}
 | 
			
		||||
-keep class org.spongycastle.** { *; }
 | 
			
		||||
-dontwarn javax.naming.**
 | 
			
		||||
 | 
			
		||||
-dontwarn android.content.**
 | 
			
		||||
-dontwarn android.animation.**
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,14 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest package="com.topjohnwu.magisk"
 | 
			
		||||
          xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
 | 
			
		||||
          xmlns:tools="http://schemas.android.com/tools">
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
    package="com.topjohnwu.magisk">
 | 
			
		||||
 | 
			
		||||
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.INTERNET" />
 | 
			
		||||
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 | 
			
		||||
    <uses-permission android:name="android.permission.VIBRATE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:name=".MagiskManager"
 | 
			
		||||
@@ -20,8 +21,7 @@
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".MainActivity"
 | 
			
		||||
            android:configChanges="orientation|screenSize"
 | 
			
		||||
            android:exported="true"/>
 | 
			
		||||
 | 
			
		||||
            android:exported="true" />
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".SplashActivity"
 | 
			
		||||
            android:configChanges="orientation|screenSize"
 | 
			
		||||
@@ -29,16 +29,22 @@
 | 
			
		||||
            android:theme="@style/SplashTheme">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.MAIN" />
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.LAUNCHER" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".AboutActivity"
 | 
			
		||||
            android:theme="@style/AppTheme.Transparent"/>
 | 
			
		||||
            android:theme="@style/AppTheme.Transparent" />
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".SettingsActivity"
 | 
			
		||||
            android:theme="@style/AppTheme.Transparent" />
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".FlashActivity"
 | 
			
		||||
            android:screenOrientation="nosensor"
 | 
			
		||||
            android:configChanges="keyboardHidden|orientation|screenSize"
 | 
			
		||||
            android:theme="@style/AppTheme.Transparent" />
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".superuser.RequestActivity"
 | 
			
		||||
            android:excludeFromRecents="true"
 | 
			
		||||
@@ -51,16 +57,27 @@
 | 
			
		||||
            android:taskAffinity="internal.superuser"
 | 
			
		||||
            android:theme="@style/SuRequest" />
 | 
			
		||||
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".superuser.SuReceiver" />
 | 
			
		||||
 | 
			
		||||
        <receiver android:name=".superuser.SuReceiver" />
 | 
			
		||||
        <receiver android:name=".receivers.BootReceiver">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.BOOT_COMPLETED" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </receiver>
 | 
			
		||||
        <receiver android:name=".receivers.PackageReceiver">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.PACKAGE_REPLACED" />
 | 
			
		||||
                <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
 | 
			
		||||
 | 
			
		||||
        <service android:name=".receivers.BootReceiver$BootupIntentService" />
 | 
			
		||||
                <data android:scheme="package" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </receiver>
 | 
			
		||||
        <receiver android:name=".receivers.ManagerUpdate" />
 | 
			
		||||
 | 
			
		||||
        <service android:name=".services.OnBootIntentService" />
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".services.UpdateCheckService"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:permission="android.permission.BIND_JOB_SERVICE" />
 | 
			
		||||
 | 
			
		||||
        <provider
 | 
			
		||||
            android:name="android.support.v4.content.FileProvider"
 | 
			
		||||
@@ -78,4 +95,4 @@
 | 
			
		||||
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
</manifest>
 | 
			
		||||
							
								
								
									
										276
									
								
								app/src/main/assets/dark.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								app/src/main/assets/dark.css
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										277
									
								
								app/src/main/assets/light.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								app/src/main/assets/light.css
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										192
									
								
								app/src/main/assets/util_functions.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								app/src/main/assets/util_functions.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,192 @@
 | 
			
		||||
##########################################################################################
 | 
			
		||||
# 
 | 
			
		||||
# Magisk General Utility Functions
 | 
			
		||||
# by topjohnwu
 | 
			
		||||
# 
 | 
			
		||||
# Used in flash_script.sh, addon.d.sh, magisk module installers, and uninstaller
 | 
			
		||||
# 
 | 
			
		||||
##########################################################################################
 | 
			
		||||
 | 
			
		||||
get_outfd() {
 | 
			
		||||
  readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null
 | 
			
		||||
  if [ "$?" -eq "0" ]; then
 | 
			
		||||
    OUTFD=0
 | 
			
		||||
 | 
			
		||||
    for FD in `ls /proc/$$/fd`; do
 | 
			
		||||
      readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null
 | 
			
		||||
      if [ "$?" -eq "0" ]; then
 | 
			
		||||
        ps | grep " 3 $FD " | grep -v grep >/dev/null
 | 
			
		||||
        if [ "$?" -eq "0" ]; then
 | 
			
		||||
          OUTFD=$FD
 | 
			
		||||
          break
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
    done
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ui_print() {
 | 
			
		||||
  if $BOOTMODE; then
 | 
			
		||||
    echo "$1"
 | 
			
		||||
  else 
 | 
			
		||||
    echo -n -e "ui_print $1\n" >> /proc/self/fd/$OUTFD
 | 
			
		||||
    echo -n -e "ui_print\n" >> /proc/self/fd/$OUTFD
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
getvar() {
 | 
			
		||||
  local VARNAME=$1
 | 
			
		||||
  local VALUE=$(eval echo \$"$VARNAME");
 | 
			
		||||
  for FILE in /dev/.magisk /data/.magisk /cache/.magisk /system/.magisk; do
 | 
			
		||||
    if [ -z "$VALUE" ]; then
 | 
			
		||||
      LINE=$(cat $FILE 2>/dev/null | grep "$VARNAME=")
 | 
			
		||||
      if [ ! -z "$LINE" ]; then
 | 
			
		||||
        VALUE=${LINE#*=}
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
  eval $VARNAME=\$VALUE
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
find_boot_image() {
 | 
			
		||||
  if [ -z "$BOOTIMAGE" ]; then
 | 
			
		||||
    for BLOCK in boot_a BOOT_A kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do
 | 
			
		||||
      BOOTIMAGE=`ls /dev/block/by-name/$BLOCK || ls /dev/block/platform/*/by-name/$BLOCK || ls /dev/block/platform/*/*/by-name/$BLOCK` 2>/dev/null
 | 
			
		||||
      [ ! -z $BOOTIMAGE ] && break
 | 
			
		||||
    done
 | 
			
		||||
  fi
 | 
			
		||||
  # Recovery fallback
 | 
			
		||||
  if [ -z "$BOOTIMAGE" ]; then
 | 
			
		||||
    for FSTAB in /etc/*fstab*; do
 | 
			
		||||
      BOOTIMAGE=`grep -E '\b/boot\b' $FSTAB | grep -v "#" | grep -oE '/dev/[a-zA-Z0-9_./-]*'`
 | 
			
		||||
      [ ! -z $BOOTIMAGE ] && break
 | 
			
		||||
    done
 | 
			
		||||
  fi
 | 
			
		||||
  [ -L "$BOOTIMAGE" ] && BOOTIMAGE=`readlink $BOOTIMAGE`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
is_mounted() {
 | 
			
		||||
  if [ ! -z "$2" ]; then
 | 
			
		||||
    cat /proc/mounts | grep $1 | grep $2, >/dev/null
 | 
			
		||||
  else
 | 
			
		||||
    cat /proc/mounts | grep $1 >/dev/null
 | 
			
		||||
  fi
 | 
			
		||||
  return $?
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
grep_prop() {
 | 
			
		||||
  REGEX="s/^$1=//p"
 | 
			
		||||
  shift
 | 
			
		||||
  FILES=$@
 | 
			
		||||
  if [ -z "$FILES" ]; then
 | 
			
		||||
    FILES='/system/build.prop'
 | 
			
		||||
  fi
 | 
			
		||||
  cat $FILES 2>/dev/null | sed -n "$REGEX" | head -n 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
remove_system_su() {
 | 
			
		||||
  if [ -f /system/bin/su -o -f /system/xbin/su ] && [ ! -f /su/bin/su ]; then
 | 
			
		||||
    ui_print "! System installed root detected, mount rw :("
 | 
			
		||||
    mount -o rw,remount /system
 | 
			
		||||
    # SuperSU
 | 
			
		||||
    if [ -e /system/bin/.ext/.su ]; then
 | 
			
		||||
      mv -f /system/bin/app_process32_original /system/bin/app_process32 2>/dev/null
 | 
			
		||||
      mv -f /system/bin/app_process64_original /system/bin/app_process64 2>/dev/null
 | 
			
		||||
      mv -f /system/bin/install-recovery_original.sh /system/bin/install-recovery.sh 2>/dev/null
 | 
			
		||||
      cd /system/bin
 | 
			
		||||
      if [ -e app_process64 ]; then
 | 
			
		||||
        ln -sf app_process64 app_process
 | 
			
		||||
      else
 | 
			
		||||
        ln -sf app_process32 app_process
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
    rm -rf /system/.pin /system/bin/.ext /system/etc/.installed_su_daemon /system/etc/.has_su_daemon \
 | 
			
		||||
    /system/xbin/daemonsu /system/xbin/su /system/xbin/sugote /system/xbin/sugote-mksh /system/xbin/supolicy \
 | 
			
		||||
    /system/bin/app_process_init /system/bin/su /cache/su /system/lib/libsupol.so /system/lib64/libsupol.so \
 | 
			
		||||
    /system/su.d /system/etc/install-recovery.sh /system/etc/init.d/99SuperSUDaemon /cache/install-recovery.sh \
 | 
			
		||||
    /system/.supersu /cache/.supersu /data/.supersu \
 | 
			
		||||
    /system/app/Superuser.apk /system/app/SuperSU /cache/Superuser.apk  2>/dev/null
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
api_level_arch_detect() {
 | 
			
		||||
  API=`grep_prop ro.build.version.sdk`
 | 
			
		||||
  ABI=`grep_prop ro.product.cpu.abi | cut -c-3`
 | 
			
		||||
  ABI2=`grep_prop ro.product.cpu.abi2 | cut -c-3`
 | 
			
		||||
  ABILONG=`grep_prop ro.product.cpu.abi`
 | 
			
		||||
 | 
			
		||||
  ARCH=arm
 | 
			
		||||
  IS64BIT=false
 | 
			
		||||
  if [ "$ABI" = "x86" ]; then ARCH=x86; fi;
 | 
			
		||||
  if [ "$ABI2" = "x86" ]; then ARCH=x86; fi;
 | 
			
		||||
  if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=true; fi;
 | 
			
		||||
  if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=true; fi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
recovery_actions() {
 | 
			
		||||
  # TWRP bug fix
 | 
			
		||||
  mount -o bind /dev/urandom /dev/random
 | 
			
		||||
  # Temporarily block out all custom recovery binaries/libs
 | 
			
		||||
  mv /sbin /sbin_tmp
 | 
			
		||||
  # Add all possible library paths
 | 
			
		||||
  OLD_LD_PATH=$LD_LIBRARY_PATH
 | 
			
		||||
  $IS64BIT && export LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64 || export LD_LIBRARY_PATH=/system/lib:/system/vendor/lib
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
recovery_cleanup() {
 | 
			
		||||
  mv /sbin_tmp /sbin
 | 
			
		||||
  # Clear LD_LIBRARY_PATH
 | 
			
		||||
  export LD_LIBRARY_PATH=$OLD_LD_PATH
 | 
			
		||||
  ui_print "- Unmounting partitions"
 | 
			
		||||
  umount -l /system
 | 
			
		||||
  umount -l /vendor 2>/dev/null
 | 
			
		||||
  umount -l /dev/random
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
abort() {
 | 
			
		||||
  ui_print "$1"
 | 
			
		||||
  mv /sbin_tmp /sbin 2>/dev/null
 | 
			
		||||
  exit 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
set_perm() {
 | 
			
		||||
  chown $2:$3 $1 || exit 1
 | 
			
		||||
  chmod $4 $1 || exit 1
 | 
			
		||||
  if [ ! -z $5 ]; then
 | 
			
		||||
    chcon $5 $1 2>/dev/null
 | 
			
		||||
  else
 | 
			
		||||
    chcon 'u:object_r:system_file:s0' $1 2>/dev/null
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
set_perm_recursive() {
 | 
			
		||||
  find $1 -type d 2>/dev/null | while read dir; do
 | 
			
		||||
    set_perm $dir $2 $3 $4 $6
 | 
			
		||||
  done
 | 
			
		||||
  find $1 -type f 2>/dev/null | while read file; do
 | 
			
		||||
    set_perm $file $2 $3 $5 $6
 | 
			
		||||
  done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mktouch() {
 | 
			
		||||
  mkdir -p ${1%/*}
 | 
			
		||||
  if [ -z "$2" ]; then
 | 
			
		||||
    touch $1
 | 
			
		||||
  else
 | 
			
		||||
    echo $2 > $1
 | 
			
		||||
  fi
 | 
			
		||||
  chmod 644 $1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
request_size_check() {
 | 
			
		||||
  reqSizeM=`du -s $1 | cut -f1`
 | 
			
		||||
  reqSizeM=$((reqSizeM / 1024 + 1))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image_size_check() {
 | 
			
		||||
  SIZE="`$MAGISKBIN/magisk --imgsize $IMG`"
 | 
			
		||||
  curUsedM=`echo "$SIZE" | cut -d" " -f1`
 | 
			
		||||
  curSizeM=`echo "$SIZE" | cut -d" " -f2`
 | 
			
		||||
  curFreeM=$((curSizeM - curUsedM))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,24 +1,22 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.support.v7.app.AlertDialog;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.preference.PreferenceManager;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.support.v7.app.ActionBar;
 | 
			
		||||
import android.support.v7.app.AlertDialog;
 | 
			
		||||
import android.support.v7.widget.Toolbar;
 | 
			
		||||
import android.text.Html;
 | 
			
		||||
import android.text.Spanned;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.text.method.LinkMovementMethod;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.WindowManager;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.components.AboutCardRow;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
@@ -28,7 +26,7 @@ import butterknife.ButterKnife;
 | 
			
		||||
 | 
			
		||||
public class AboutActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    private static final String DONATION_URL = "http://topjohnwu.github.io/donate";
 | 
			
		||||
    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";
 | 
			
		||||
 | 
			
		||||
@@ -44,10 +42,8 @@ public class AboutActivity extends Activity {
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(@Nullable Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
 | 
			
		||||
        Logger.dev("AboutActivity: Theme is " + theme);
 | 
			
		||||
        if (getTopApplication().isDarkTheme) {
 | 
			
		||||
            setTheme(R.style.AppTheme_dh);
 | 
			
		||||
        if (getApplicationContext().isDarkTheme) {
 | 
			
		||||
            setTheme(R.style.AppTheme_Transparent_Dark);
 | 
			
		||||
        }
 | 
			
		||||
        setContentView(R.layout.activity_about);
 | 
			
		||||
        ButterKnife.bind(this);
 | 
			
		||||
@@ -85,7 +81,7 @@ public class AboutActivity extends Activity {
 | 
			
		||||
                result = Html.fromHtml(changes);
 | 
			
		||||
            }
 | 
			
		||||
            appChangelog.setOnClickListener(v -> {
 | 
			
		||||
                AlertDialog d = Utils.getAlertDialogBuilder(this)
 | 
			
		||||
                AlertDialog d = new AlertDialogBuilder(this)
 | 
			
		||||
                        .setTitle(R.string.app_changelog)
 | 
			
		||||
                        .setMessage(result)
 | 
			
		||||
                        .setPositiveButton(android.R.string.ok, null)
 | 
			
		||||
@@ -104,7 +100,7 @@ public class AboutActivity extends Activity {
 | 
			
		||||
            } else {
 | 
			
		||||
                result = Html.fromHtml(getString(R.string.app_developers_));
 | 
			
		||||
            }
 | 
			
		||||
            AlertDialog d = Utils.getAlertDialogBuilder(this)
 | 
			
		||||
            AlertDialog d = new AlertDialogBuilder(this)
 | 
			
		||||
                    .setTitle(R.string.app_developers)
 | 
			
		||||
                    .setMessage(result)
 | 
			
		||||
                    .setPositiveButton(android.R.string.ok, null)
 | 
			
		||||
@@ -134,18 +130,4 @@ public class AboutActivity extends Activity {
 | 
			
		||||
        setFloating();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setFloating() {
 | 
			
		||||
        boolean isTablet = getResources().getBoolean(R.bool.isTablet);
 | 
			
		||||
        if (isTablet) {
 | 
			
		||||
            WindowManager.LayoutParams params = getWindow().getAttributes();
 | 
			
		||||
            params.height = getResources().getDimensionPixelSize(R.dimen.floating_height);
 | 
			
		||||
            params.width = getResources().getDimensionPixelSize(R.dimen.floating_width);
 | 
			
		||||
            params.alpha = 1.0f;
 | 
			
		||||
            params.dimAmount = 0.6f;
 | 
			
		||||
            params.flags |= 2;
 | 
			
		||||
            getWindow().setAttributes(params);
 | 
			
		||||
            setFinishOnTouchOutside(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								app/src/main/java/com/topjohnwu/magisk/FlashActivity.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								app/src/main/java/com/topjohnwu/magisk/FlashActivity.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.support.v7.app.ActionBar;
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.support.v7.widget.Toolbar;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.FlashZip;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.utils.AdaptiveList;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
import butterknife.OnClick;
 | 
			
		||||
 | 
			
		||||
public class FlashActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    @BindView(R.id.toolbar) Toolbar toolbar;
 | 
			
		||||
    @BindView(R.id.flash_logs) RecyclerView flashLogs;
 | 
			
		||||
    @BindView(R.id.button_panel) LinearLayout buttonPanel;
 | 
			
		||||
 | 
			
		||||
    private AdaptiveList<String> rootShellOutput;
 | 
			
		||||
 | 
			
		||||
    @OnClick(R.id.no_thanks)
 | 
			
		||||
    public void dismiss() {
 | 
			
		||||
        finish();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @OnClick(R.id.reboot)
 | 
			
		||||
    public void reboot() {
 | 
			
		||||
        Shell.getShell(this).su_raw("reboot");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setContentView(R.layout.activity_flash);
 | 
			
		||||
        ButterKnife.bind(this);
 | 
			
		||||
        rootShellOutput = new AdaptiveList<>(flashLogs);
 | 
			
		||||
        setSupportActionBar(toolbar);
 | 
			
		||||
        ActionBar ab = getSupportActionBar();
 | 
			
		||||
        if (ab != null) {
 | 
			
		||||
            ab.setTitle(R.string.flashing);
 | 
			
		||||
        }
 | 
			
		||||
        setFloating();
 | 
			
		||||
 | 
			
		||||
        flashLogs.setAdapter(new FlashLogAdapter());
 | 
			
		||||
 | 
			
		||||
        // We must receive a Uri of the target zip
 | 
			
		||||
        Uri uri = getIntent().getData();
 | 
			
		||||
 | 
			
		||||
        new FlashZip(this, uri, rootShellOutput)
 | 
			
		||||
                .setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE))
 | 
			
		||||
                .exec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBackPressed() {
 | 
			
		||||
        // Prevent user accidentally press back button
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class FlashLogAdapter extends RecyclerView.Adapter<ViewHolder> {
 | 
			
		||||
 | 
			
		||||
        @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(rootShellOutput.get(position));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int getItemCount() {
 | 
			
		||||
            return rootShellOutput.size();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class ViewHolder extends RecyclerView.ViewHolder {
 | 
			
		||||
 | 
			
		||||
        @BindView(R.id.textView) TextView text;
 | 
			
		||||
 | 
			
		||||
        public ViewHolder(View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
            ButterKnife.bind(this, itemView);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,174 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.app.ProgressDialog;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.CountDownTimer;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.support.v7.widget.CardView;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.ArrayAdapter;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.CheckBox;
 | 
			
		||||
import android.widget.Spinner;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.receivers.MagiskDlReceiver;
 | 
			
		||||
import com.topjohnwu.magisk.utils.CallbackEvent;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
import butterknife.Unbinder;
 | 
			
		||||
 | 
			
		||||
public class InstallFragment extends Fragment implements CallbackEvent.Listener<Void> {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private static final String UNINSTALLER = "magisk_uninstaller.sh";
 | 
			
		||||
 | 
			
		||||
    private Unbinder unbinder;
 | 
			
		||||
    @BindView(R.id.current_version_title) TextView currentVersionTitle;
 | 
			
		||||
    @BindView(R.id.install_title) TextView installTitle;
 | 
			
		||||
    @BindView(R.id.block_spinner) Spinner spinner;
 | 
			
		||||
    @BindView(R.id.detect_bootimage) Button detectButton;
 | 
			
		||||
    @BindView(R.id.flash_button) CardView flashButton;
 | 
			
		||||
    @BindView(R.id.uninstall_button) CardView uninstallButton;
 | 
			
		||||
    @BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
 | 
			
		||||
    @BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 | 
			
		||||
        View v = inflater.inflate(R.layout.fragment_install, container, false);
 | 
			
		||||
        unbinder = ButterKnife.bind(this, v);
 | 
			
		||||
        detectButton.setOnClickListener(v1 -> toAutoDetect());
 | 
			
		||||
        currentVersionTitle.setText(getString(R.string.current_magisk_title, getApplication().magiskVersionString));
 | 
			
		||||
        installTitle.setText(getString(R.string.install_magisk_title, getApplication().remoteMagiskVersion));
 | 
			
		||||
        flashButton.setOnClickListener(v1 -> {
 | 
			
		||||
            String bootImage;
 | 
			
		||||
            if (getApplication().bootBlock != null) {
 | 
			
		||||
                if (spinner.getSelectedItemPosition() > 0)
 | 
			
		||||
                    bootImage = getApplication().blockList.get(spinner.getSelectedItemPosition() - 1);
 | 
			
		||||
                else
 | 
			
		||||
                    bootImage = getApplication().bootBlock;
 | 
			
		||||
            } else {
 | 
			
		||||
                bootImage = getApplication().blockList.get(spinner.getSelectedItemPosition());
 | 
			
		||||
            }
 | 
			
		||||
            String filename = "Magisk-v" + getApplication().remoteMagiskVersion + ".zip";
 | 
			
		||||
            Utils.getAlertDialogBuilder(getActivity())
 | 
			
		||||
                    .setTitle(getString(R.string.repo_install_title, getString(R.string.magisk)))
 | 
			
		||||
                    .setMessage(getString(R.string.repo_install_msg, filename))
 | 
			
		||||
                    .setCancelable(true)
 | 
			
		||||
                    .setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
 | 
			
		||||
                            getActivity(),
 | 
			
		||||
                            new MagiskDlReceiver(bootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()),
 | 
			
		||||
                            getApplication().magiskLink,
 | 
			
		||||
                            Utils.getLegalFilename(filename)))
 | 
			
		||||
                    .setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
 | 
			
		||||
                        getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getApplication().releaseNoteLink)));
 | 
			
		||||
                    })
 | 
			
		||||
                    .setNegativeButton(R.string.no_thanks, null)
 | 
			
		||||
                    .show();
 | 
			
		||||
        });
 | 
			
		||||
        if (getApplication().magiskVersion < 10.3) {
 | 
			
		||||
            uninstallButton.setVisibility(View.GONE);
 | 
			
		||||
        } else {
 | 
			
		||||
            uninstallButton.setOnClickListener(vi -> {
 | 
			
		||||
                Utils.getAlertDialogBuilder(getActivity())
 | 
			
		||||
                        .setTitle("Uninstall Magisk")
 | 
			
		||||
                        .setMessage("This will remove all modules, MagiskSU, and potentially re-encrypt your device\nAre you sure to process?")
 | 
			
		||||
                        .setPositiveButton(R.string.yes, (dialogInterface, i) -> {
 | 
			
		||||
                            try {
 | 
			
		||||
                                InputStream in = getActivity().getAssets().open(UNINSTALLER);
 | 
			
		||||
                                File uninstaller = new File(getActivity().getCacheDir().getAbsolutePath() + "/" + UNINSTALLER);
 | 
			
		||||
                                FileOutputStream out = new FileOutputStream(uninstaller);
 | 
			
		||||
                                byte[] bytes = new byte[1024];
 | 
			
		||||
                                int read;
 | 
			
		||||
                                while ((read = in.read(bytes)) != -1)
 | 
			
		||||
                                    out.write(bytes, 0, read);
 | 
			
		||||
                                in.close();
 | 
			
		||||
                                out.close();
 | 
			
		||||
                                ProgressDialog progress = new ProgressDialog(getActivity());
 | 
			
		||||
                                progress.setTitle(R.string.reboot);
 | 
			
		||||
                                progress.show();
 | 
			
		||||
                                new CountDownTimer(5000, 1000) {
 | 
			
		||||
                                    @Override
 | 
			
		||||
                                    public void onTick(long millisUntilFinished) {
 | 
			
		||||
                                        progress.setMessage(getString(R.string.reboot_countdown, millisUntilFinished / 1000));
 | 
			
		||||
                                    }
 | 
			
		||||
 | 
			
		||||
                                    @Override
 | 
			
		||||
                                    public void onFinish() {
 | 
			
		||||
                                        progress.setMessage(getString(R.string.reboot_countdown, 0));
 | 
			
		||||
                                        Shell.su(true, "cp -af " + uninstaller + " /cache/" + UNINSTALLER,
 | 
			
		||||
                                                "reboot");
 | 
			
		||||
                                    }
 | 
			
		||||
                                }.start();
 | 
			
		||||
                            } catch (IOException e) {
 | 
			
		||||
                                e.printStackTrace();
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                        .setNegativeButton(R.string.no_thanks, null)
 | 
			
		||||
                        .show();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (getApplication().blockDetectionDone.isTriggered) {
 | 
			
		||||
            updateUI();
 | 
			
		||||
        }
 | 
			
		||||
        return v;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTrigger(CallbackEvent<Void> event) {
 | 
			
		||||
        updateUI();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateUI() {
 | 
			
		||||
        List<String> items = new ArrayList<>(getApplication().blockList);
 | 
			
		||||
        if (getApplication().bootBlock != null)
 | 
			
		||||
            items.add(0, getString(R.string.auto_detect, getApplication().bootBlock));
 | 
			
		||||
        ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
 | 
			
		||||
                android.R.layout.simple_spinner_item, items);
 | 
			
		||||
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 | 
			
		||||
        spinner.setAdapter(adapter);
 | 
			
		||||
        toAutoDetect();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void toAutoDetect() {
 | 
			
		||||
        if (getApplication().bootBlock != null) {
 | 
			
		||||
            spinner.setSelection(0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStart() {
 | 
			
		||||
        super.onStart();
 | 
			
		||||
        getActivity().setTitle(R.string.install);
 | 
			
		||||
        getApplication().blockDetectionDone.register(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStop() {
 | 
			
		||||
        getApplication().blockDetectionDone.unRegister(this);
 | 
			
		||||
        super.onStop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onDestroyView() {
 | 
			
		||||
        super.onDestroyView();
 | 
			
		||||
        unbinder.unbind();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -31,14 +31,14 @@ public class LogFragment extends Fragment {
 | 
			
		||||
 | 
			
		||||
        TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
 | 
			
		||||
 | 
			
		||||
        adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
 | 
			
		||||
 | 
			
		||||
        if (getApplication().isSuClient) {
 | 
			
		||||
            adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
 | 
			
		||||
            tab.setupWithViewPager(viewPager);
 | 
			
		||||
            tab.setVisibility(View.VISIBLE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
 | 
			
		||||
 | 
			
		||||
        viewPager.setAdapter(adapter);
 | 
			
		||||
 | 
			
		||||
        return v;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										505
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										505
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -15,8 +15,8 @@ import android.view.ViewGroup;
 | 
			
		||||
import android.widget.SearchView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.MagiskHide;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Async;
 | 
			
		||||
import com.topjohnwu.magisk.utils.CallbackEvent;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
 | 
			
		||||
@@ -50,7 +50,7 @@ public class MagiskHideFragment extends Fragment implements CallbackEvent.Listen
 | 
			
		||||
        PackageManager packageManager = getActivity().getPackageManager();
 | 
			
		||||
 | 
			
		||||
        mSwipeRefreshLayout.setRefreshing(true);
 | 
			
		||||
        mSwipeRefreshLayout.setOnRefreshListener(() -> new Async.LoadApps(getApplication()).exec());
 | 
			
		||||
        mSwipeRefreshLayout.setOnRefreshListener(() -> new MagiskHide(getActivity()).list());
 | 
			
		||||
 | 
			
		||||
        appAdapter = new ApplicationAdapter(packageManager);
 | 
			
		||||
        recyclerView.setAdapter(appAdapter);
 | 
			
		||||
@@ -71,8 +71,9 @@ public class MagiskHideFragment extends Fragment implements CallbackEvent.Listen
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (getApplication().packageLoadDone.isTriggered)
 | 
			
		||||
            onTrigger(getApplication().packageLoadDone);
 | 
			
		||||
        if (getApplication().magiskHideDone.isTriggered) {
 | 
			
		||||
            onTrigger(getApplication().magiskHideDone);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return view;
 | 
			
		||||
    }
 | 
			
		||||
@@ -88,12 +89,12 @@ public class MagiskHideFragment extends Fragment implements CallbackEvent.Listen
 | 
			
		||||
    public void onStart() {
 | 
			
		||||
        super.onStart();
 | 
			
		||||
        getActivity().setTitle(R.string.magiskhide);
 | 
			
		||||
        getApplication().packageLoadDone.register(this);
 | 
			
		||||
        getApplication().magiskHideDone.register(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStop() {
 | 
			
		||||
        getApplication().packageLoadDone.unRegister(this);
 | 
			
		||||
        getApplication().magiskHideDone.unRegister(this);
 | 
			
		||||
        super.onStop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,13 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.Manifest;
 | 
			
		||||
import android.annotation.SuppressLint;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Environment;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.support.design.widget.Snackbar;
 | 
			
		||||
import android.support.v4.app.ActivityCompat;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
@@ -24,16 +21,15 @@ import android.widget.ScrollView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Async;
 | 
			
		||||
import com.topjohnwu.magisk.components.SnackbarMaker;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileWriter;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Calendar;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
@@ -121,49 +117,33 @@ public class MagiskLogFragment extends Fragment {
 | 
			
		||||
                    new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                Snackbar.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
 | 
			
		||||
                SnackbarMaker.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class LogManager extends Async.RootTask<Object, Void, Object> {
 | 
			
		||||
    private class LogManager extends ParallelTask<Object, Void, Object> {
 | 
			
		||||
 | 
			
		||||
        int mode;
 | 
			
		||||
        File targetFile;
 | 
			
		||||
        private int mode;
 | 
			
		||||
        private File targetFile;
 | 
			
		||||
 | 
			
		||||
        @SuppressLint("DefaultLocale")
 | 
			
		||||
        @Override
 | 
			
		||||
        protected Object doInBackground(Object... params) {
 | 
			
		||||
            MagiskManager magiskManager = MagiskLogFragment.this.getApplication();
 | 
			
		||||
            mode = (int) params[0];
 | 
			
		||||
            switch (mode) {
 | 
			
		||||
                case 0:
 | 
			
		||||
                    List<String> logList = Utils.readFile(MAGISK_LOG);
 | 
			
		||||
 | 
			
		||||
                    if (Utils.isValidShellResponse(logList)) {
 | 
			
		||||
                        StringBuilder llog = new StringBuilder(15 * 10 * 1024);
 | 
			
		||||
                        for (String s : logList) {
 | 
			
		||||
                            llog.append(s).append("\n");
 | 
			
		||||
                        }
 | 
			
		||||
                        return llog.toString();
 | 
			
		||||
                    }
 | 
			
		||||
                    return "";
 | 
			
		||||
                    StringBuildingList logList = new StringBuildingList();
 | 
			
		||||
                    magiskManager.shell.su(logList, "cat " + MAGISK_LOG);
 | 
			
		||||
                    return logList.toString();
 | 
			
		||||
 | 
			
		||||
                case 1:
 | 
			
		||||
                    Shell.su("echo > " + MAGISK_LOG);
 | 
			
		||||
                    Snackbar.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
                    magiskManager.shell.su_raw("echo > " + MAGISK_LOG);
 | 
			
		||||
                    SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
                    return "";
 | 
			
		||||
 | 
			
		||||
                case 2:
 | 
			
		||||
                    if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
 | 
			
		||||
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 | 
			
		||||
                            requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
 | 
			
		||||
                        }
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
 | 
			
		||||
                        return false;
 | 
			
		||||
 | 
			
		||||
                    Calendar now = Calendar.getInstance();
 | 
			
		||||
                    String filename = String.format(
 | 
			
		||||
                            "magisk_%s_%04d%02d%02d_%02d%02d%02d.log", "error",
 | 
			
		||||
@@ -174,22 +154,18 @@ public class MagiskLogFragment extends Fragment {
 | 
			
		||||
                    targetFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/MagiskManager/" + filename);
 | 
			
		||||
 | 
			
		||||
                    if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs())
 | 
			
		||||
                            || (targetFile.exists() && !targetFile.delete()))
 | 
			
		||||
                            || (targetFile.exists() && !targetFile.delete())) {
 | 
			
		||||
                        return false;
 | 
			
		||||
 | 
			
		||||
                    List<String> in = Utils.readFile(MAGISK_LOG);
 | 
			
		||||
 | 
			
		||||
                    if (Utils.isValidShellResponse(in)) {
 | 
			
		||||
                        try (FileWriter out = new FileWriter(targetFile)) {
 | 
			
		||||
                            for (String line : in)
 | 
			
		||||
                                out.write(line + "\n");
 | 
			
		||||
                            return true;
 | 
			
		||||
                        } catch (IOException e) {
 | 
			
		||||
                            e.printStackTrace();
 | 
			
		||||
                            return false;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    return false;
 | 
			
		||||
 | 
			
		||||
                    try (FileWriter out = new FileWriter(targetFile)) {
 | 
			
		||||
                        FileWritingList fileWritingList = new FileWritingList(out);
 | 
			
		||||
                        magiskManager.shell.su(fileWritingList, "cat " + MAGISK_LOG);
 | 
			
		||||
                    } catch (IOException e) {
 | 
			
		||||
                        e.printStackTrace();
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                    return true;
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
@@ -197,12 +173,10 @@ public class MagiskLogFragment extends Fragment {
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void onPostExecute(Object o) {
 | 
			
		||||
            if (o == null) return;
 | 
			
		||||
            boolean bool;
 | 
			
		||||
            String llog;
 | 
			
		||||
            switch (mode) {
 | 
			
		||||
                case 0:
 | 
			
		||||
                case 1:
 | 
			
		||||
                    llog = (String) o;
 | 
			
		||||
                    String llog = (String) o;
 | 
			
		||||
                    progressBar.setVisibility(View.GONE);
 | 
			
		||||
                    if (TextUtils.isEmpty(llog))
 | 
			
		||||
                        txtLog.setText(R.string.log_is_empty);
 | 
			
		||||
@@ -212,26 +186,64 @@ public class MagiskLogFragment extends Fragment {
 | 
			
		||||
                    hsvLog.post(() -> hsvLog.scrollTo(0, 0));
 | 
			
		||||
                    break;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    bool = (boolean) o;
 | 
			
		||||
                    if (bool)
 | 
			
		||||
                        Toast.makeText(getActivity(), targetFile.toString(), Toast.LENGTH_LONG).show();
 | 
			
		||||
                    else
 | 
			
		||||
                        Toast.makeText(getActivity(), getString(R.string.logs_save_failed), Toast.LENGTH_LONG).show();
 | 
			
		||||
                    boolean bool = (boolean) o;
 | 
			
		||||
                    if (bool) {
 | 
			
		||||
                        MagiskLogFragment.this.getApplication().toast(targetFile.toString(), Toast.LENGTH_LONG);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        MagiskLogFragment.this.getApplication().toast(R.string.logs_save_failed, Toast.LENGTH_LONG);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void read() {
 | 
			
		||||
        void read() {
 | 
			
		||||
            exec(0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void clear() {
 | 
			
		||||
        void clear() {
 | 
			
		||||
            exec(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void save() {
 | 
			
		||||
        void save() {
 | 
			
		||||
            exec(2);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class StringBuildingList extends Shell.AbstractList<String> {
 | 
			
		||||
 | 
			
		||||
        StringBuilder builder;
 | 
			
		||||
 | 
			
		||||
        StringBuildingList() {
 | 
			
		||||
            builder = new StringBuilder();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean add(String s) {
 | 
			
		||||
            builder.append(s).append("\n");
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public String toString() {
 | 
			
		||||
            return builder.toString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class FileWritingList extends Shell.AbstractList<String> {
 | 
			
		||||
 | 
			
		||||
        private FileWriter writer;
 | 
			
		||||
 | 
			
		||||
        FileWritingList(FileWriter out) {
 | 
			
		||||
            writer = out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public boolean add(String s) {
 | 
			
		||||
            try {
 | 
			
		||||
                writer.write(s + "\n");
 | 
			
		||||
            } catch (IOException ignored) {}
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -7,7 +7,6 @@ import android.content.pm.PackageManager;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.preference.PreferenceManager;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
import android.support.design.widget.NavigationView;
 | 
			
		||||
import android.support.v4.app.ActivityCompat;
 | 
			
		||||
@@ -23,6 +22,7 @@ import android.view.View;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.utils.CallbackEvent;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
@@ -32,6 +32,7 @@ public class MainActivity extends Activity
 | 
			
		||||
 | 
			
		||||
    private final Handler mDrawerHandler = new Handler();
 | 
			
		||||
    private SharedPreferences prefs;
 | 
			
		||||
    private int mDrawerItem;
 | 
			
		||||
 | 
			
		||||
    @BindView(R.id.toolbar) Toolbar toolbar;
 | 
			
		||||
    @BindView(R.id.drawer_layout) DrawerLayout drawer;
 | 
			
		||||
@@ -42,10 +43,10 @@ public class MainActivity extends Activity
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(final Bundle savedInstanceState) {
 | 
			
		||||
 | 
			
		||||
        prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
 | 
			
		||||
        prefs = getApplicationContext().prefs;
 | 
			
		||||
 | 
			
		||||
        if (getTopApplication().isDarkTheme) {
 | 
			
		||||
            setTheme(R.style.AppTheme_dh);
 | 
			
		||||
        if (getApplicationContext().isDarkTheme) {
 | 
			
		||||
            setTheme(R.style.AppTheme_Dark);
 | 
			
		||||
        }
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setContentView(R.layout.activity_main);
 | 
			
		||||
@@ -76,40 +77,35 @@ public class MainActivity extends Activity
 | 
			
		||||
        drawer.addDrawerListener(toggle);
 | 
			
		||||
        toggle.syncState();
 | 
			
		||||
 | 
			
		||||
        navigate(R.id.status);
 | 
			
		||||
        if (savedInstanceState == null)
 | 
			
		||||
            navigate(getIntent().getStringExtra(MagiskManager.INTENT_SECTION));
 | 
			
		||||
 | 
			
		||||
        navigationView.setNavigationItemSelectedListener(this);
 | 
			
		||||
        getTopApplication().reloadMainActivity.register(this);
 | 
			
		||||
        getApplicationContext().reloadMainActivity.register(this);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onResume() {
 | 
			
		||||
        super.onResume();
 | 
			
		||||
        getTopApplication().updateCheckDone.register(this);
 | 
			
		||||
//        if (getTopApplication().updateCheckDone.isTriggered)
 | 
			
		||||
//            onTrigger(getTopApplication().updateCheckDone);
 | 
			
		||||
        checkHideSection();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPause() {
 | 
			
		||||
        getTopApplication().updateCheckDone.unRegister(this);
 | 
			
		||||
        super.onPause();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
        getTopApplication().reloadMainActivity.unRegister(this);
 | 
			
		||||
        getApplicationContext().reloadMainActivity.unRegister(this);
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBackPressed() {
 | 
			
		||||
        if (drawer.isDrawerOpen(navigationView))
 | 
			
		||||
        if (drawer.isDrawerOpen(navigationView)) {
 | 
			
		||||
            drawer.closeDrawer(navigationView);
 | 
			
		||||
        else
 | 
			
		||||
        } else if (mDrawerItem != R.id.magisk) {
 | 
			
		||||
            navigate(R.id.magisk);
 | 
			
		||||
        } else {
 | 
			
		||||
            finish();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -122,34 +118,73 @@ public class MainActivity extends Activity
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTrigger(CallbackEvent<Void> event) {
 | 
			
		||||
        if (event == getTopApplication().updateCheckDone) {
 | 
			
		||||
            Menu menu = navigationView.getMenu();
 | 
			
		||||
            menu.findItem(R.id.install).setVisible(
 | 
			
		||||
                    getTopApplication().remoteMagiskVersion > 0 && Shell.rootAccess());
 | 
			
		||||
        } else if (event == getTopApplication().reloadMainActivity) {
 | 
			
		||||
            recreate();
 | 
			
		||||
        }
 | 
			
		||||
        recreate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void checkHideSection() {
 | 
			
		||||
    public void checkHideSection() {
 | 
			
		||||
        Menu menu = navigationView.getMenu();
 | 
			
		||||
        if (Shell.rootAccess()) {
 | 
			
		||||
            menu.findItem(R.id.magiskhide).setVisible(
 | 
			
		||||
                    getTopApplication().magiskVersion >= 8 && prefs.getBoolean("magiskhide", false));
 | 
			
		||||
            menu.findItem(R.id.modules).setVisible(getTopApplication().magiskVersion >= 4);
 | 
			
		||||
            menu.findItem(R.id.downloads).setVisible(getTopApplication().magiskVersion >= 4);
 | 
			
		||||
            menu.findItem(R.id.log).setVisible(true);
 | 
			
		||||
            menu.findItem(R.id.superuser).setVisible(getTopApplication().isSuClient);
 | 
			
		||||
        menu.findItem(R.id.magiskhide).setVisible(
 | 
			
		||||
                Shell.rootAccess() && getApplicationContext().magiskVersionCode >= 1300
 | 
			
		||||
                        && prefs.getBoolean("magiskhide", false));
 | 
			
		||||
        menu.findItem(R.id.modules).setVisible(
 | 
			
		||||
                Shell.rootAccess() && getApplicationContext().magiskVersionCode >= 0);
 | 
			
		||||
        menu.findItem(R.id.downloads).setVisible(Utils.checkNetworkStatus(this) &&
 | 
			
		||||
                Shell.rootAccess() && getApplicationContext().magiskVersionCode >= 0);
 | 
			
		||||
        menu.findItem(R.id.log).setVisible(Shell.rootAccess());
 | 
			
		||||
        menu.findItem(R.id.superuser).setVisible(
 | 
			
		||||
                Shell.rootAccess() && getApplicationContext().isSuClient);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void navigate(String item) {
 | 
			
		||||
        int itemId = R.id.magisk;
 | 
			
		||||
        if (item != null) {
 | 
			
		||||
            switch (item) {
 | 
			
		||||
                case "magisk":
 | 
			
		||||
                    itemId = R.id.magisk;
 | 
			
		||||
                    break;
 | 
			
		||||
                case "install":
 | 
			
		||||
                    itemId = -1;
 | 
			
		||||
                    break;
 | 
			
		||||
                case "superuser":
 | 
			
		||||
                    itemId = R.id.superuser;
 | 
			
		||||
                    break;
 | 
			
		||||
                case "modules":
 | 
			
		||||
                    itemId = R.id.modules;
 | 
			
		||||
                    break;
 | 
			
		||||
                case "downloads":
 | 
			
		||||
                    itemId = R.id.downloads;
 | 
			
		||||
                    break;
 | 
			
		||||
                case "magiskhide":
 | 
			
		||||
                    itemId = R.id.magiskhide;
 | 
			
		||||
                    break;
 | 
			
		||||
                case "log":
 | 
			
		||||
                    itemId = R.id.log;
 | 
			
		||||
                    break;
 | 
			
		||||
                case "settings":
 | 
			
		||||
                    itemId = R.id.settings;
 | 
			
		||||
                    break;
 | 
			
		||||
                case "about":
 | 
			
		||||
                    itemId = R.id.app_about;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        navigate(itemId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void navigate(int itemId) {
 | 
			
		||||
        int bak = mDrawerItem;
 | 
			
		||||
        mDrawerItem = itemId;
 | 
			
		||||
        navigationView.setCheckedItem(itemId);
 | 
			
		||||
        switch (itemId) {
 | 
			
		||||
            case R.id.status:
 | 
			
		||||
                displayFragment(new StatusFragment(), "status", true);
 | 
			
		||||
            case -1:
 | 
			
		||||
                Bundle args = new Bundle();
 | 
			
		||||
                args.putBoolean(MagiskFragment.SHOW_DIALOG, true);
 | 
			
		||||
                Fragment frag = new MagiskFragment();
 | 
			
		||||
                frag.setArguments(args);
 | 
			
		||||
                displayFragment(frag, "magisk", true);
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.install:
 | 
			
		||||
                displayFragment(new InstallFragment(), "install", true);
 | 
			
		||||
            case R.id.magisk:
 | 
			
		||||
                displayFragment(new MagiskFragment(), "magisk", true);
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.superuser:
 | 
			
		||||
                displayFragment(new SuperuserFragment(), "superuser", true);
 | 
			
		||||
@@ -165,13 +200,14 @@ public class MainActivity extends Activity
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.log:
 | 
			
		||||
                displayFragment(new LogFragment(), "log", false);
 | 
			
		||||
                toolbar.setElevation(0);
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.settings:
 | 
			
		||||
                startActivity(new Intent(this, SettingsActivity.class));
 | 
			
		||||
                mDrawerItem = bak;
 | 
			
		||||
                break;
 | 
			
		||||
            case R.id.app_about:
 | 
			
		||||
                startActivity(new Intent(this, AboutActivity.class));
 | 
			
		||||
                mDrawerItem = bak;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -182,5 +218,6 @@ public class MainActivity extends Activity
 | 
			
		||||
        transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
 | 
			
		||||
        transaction.replace(R.id.content_frame, navFragment, tag).commitNow();
 | 
			
		||||
        if (setElevation) toolbar.setElevation(toolbarElevation);
 | 
			
		||||
        else toolbar.setElevation(0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,9 @@ package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.support.design.widget.FloatingActionButton;
 | 
			
		||||
import android.support.v4.widget.SwipeRefreshLayout;
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
@@ -12,11 +12,10 @@ import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.github.clans.fab.FloatingActionButton;
 | 
			
		||||
import com.topjohnwu.magisk.adapters.ModulesAdapter;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.LoadModules;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.module.Module;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Async;
 | 
			
		||||
import com.topjohnwu.magisk.utils.CallbackEvent;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
 | 
			
		||||
@@ -53,7 +52,7 @@ public class ModulesFragment extends Fragment implements CallbackEvent.Listener<
 | 
			
		||||
 | 
			
		||||
        mSwipeRefreshLayout.setOnRefreshListener(() -> {
 | 
			
		||||
            recyclerView.setVisibility(View.GONE);
 | 
			
		||||
            new Async.LoadModules(getApplication()).exec();
 | 
			
		||||
            new LoadModules(getActivity()).exec();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
 | 
			
		||||
@@ -85,8 +84,9 @@ public class ModulesFragment extends Fragment implements CallbackEvent.Listener<
 | 
			
		||||
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
 | 
			
		||||
        if (requestCode == FETCH_ZIP_CODE && resultCode == Activity.RESULT_OK && data != null) {
 | 
			
		||||
            // Get the URI of the selected file
 | 
			
		||||
            final Uri uri = data.getData();
 | 
			
		||||
            new Async.FlashZIP(getActivity(), uri).exec();
 | 
			
		||||
            Intent intent = new Intent(getActivity(), FlashActivity.class);
 | 
			
		||||
            intent.setData(data.getData()).putExtra("ACTION", "flash");
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.support.v4.view.MenuItemCompat;
 | 
			
		||||
import android.support.v4.widget.SwipeRefreshLayout;
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
@@ -14,17 +13,11 @@ import android.widget.SearchView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.adapters.ReposAdapter;
 | 
			
		||||
import com.topjohnwu.magisk.adapters.SimpleSectionedRecyclerViewAdapter;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.LoadRepos;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.module.Module;
 | 
			
		||||
import com.topjohnwu.magisk.module.Repo;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Async;
 | 
			
		||||
import com.topjohnwu.magisk.utils.CallbackEvent;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
import butterknife.Unbinder;
 | 
			
		||||
@@ -36,16 +29,7 @@ public class ReposFragment extends Fragment implements CallbackEvent.Listener<Vo
 | 
			
		||||
    @BindView(R.id.empty_rv) TextView emptyRv;
 | 
			
		||||
    @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
 | 
			
		||||
 | 
			
		||||
    private List<Repo> mUpdateRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> mInstalledRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> mOthersRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> fUpdateRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> fInstalledRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> fOthersRepos = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    private SimpleSectionedRecyclerViewAdapter mSectionedAdapter;
 | 
			
		||||
 | 
			
		||||
    private SearchView.OnQueryTextListener searchListener;
 | 
			
		||||
    private ReposAdapter adapter;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate(@Nullable Bundle savedInstanceState) {
 | 
			
		||||
@@ -59,51 +43,54 @@ public class ReposFragment extends Fragment implements CallbackEvent.Listener<Vo
 | 
			
		||||
        View view = inflater.inflate(R.layout.fragment_repos, container, false);
 | 
			
		||||
        unbinder = ButterKnife.bind(this, view);
 | 
			
		||||
 | 
			
		||||
        mSectionedAdapter = new SimpleSectionedRecyclerViewAdapter(R.layout.section,
 | 
			
		||||
                R.id.section_text, new ReposAdapter(fUpdateRepos, fInstalledRepos, fOthersRepos));
 | 
			
		||||
        adapter = new ReposAdapter(getApplication().repoMap);
 | 
			
		||||
 | 
			
		||||
        recyclerView.setAdapter(mSectionedAdapter);
 | 
			
		||||
        recyclerView.setAdapter(adapter);
 | 
			
		||||
 | 
			
		||||
        mSwipeRefreshLayout.setRefreshing(true);
 | 
			
		||||
 | 
			
		||||
        mSwipeRefreshLayout.setOnRefreshListener(() -> {
 | 
			
		||||
            recyclerView.setVisibility(View.GONE);
 | 
			
		||||
            new Async.LoadRepos(getApplication()).exec();
 | 
			
		||||
            new LoadRepos(getActivity()).exec();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (getApplication().repoLoadDone.isTriggered) {
 | 
			
		||||
            reloadRepos();
 | 
			
		||||
            updateUI();
 | 
			
		||||
            onTrigger(null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        searchListener = new SearchView.OnQueryTextListener() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean onQueryTextSubmit(String query) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean onQueryTextChange(String newText) {
 | 
			
		||||
                new FilterApps().exec(newText);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return view;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTrigger(CallbackEvent<Void> event) {
 | 
			
		||||
        Logger.dev("ReposFragment: UI refresh triggered");
 | 
			
		||||
        reloadRepos();
 | 
			
		||||
        updateUI();
 | 
			
		||||
        if (getApplication().repoMap.isEmpty()) {
 | 
			
		||||
            recyclerView.setVisibility(View.GONE);
 | 
			
		||||
            emptyRv.setVisibility(View.VISIBLE);
 | 
			
		||||
        } else {
 | 
			
		||||
            adapter.filter(getApplication().moduleMap, "");
 | 
			
		||||
            recyclerView.setVisibility(View.VISIBLE);
 | 
			
		||||
            emptyRv.setVisibility(View.GONE);
 | 
			
		||||
            mSwipeRefreshLayout.setRefreshing(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
 | 
			
		||||
        inflater.inflate(R.menu.menu_repo, menu);
 | 
			
		||||
        SearchView search = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.repo_search));
 | 
			
		||||
        search.setOnQueryTextListener(searchListener);
 | 
			
		||||
        SearchView search = (SearchView) menu.findItem(R.id.repo_search).getActionView();
 | 
			
		||||
        search.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean onQueryTextSubmit(String query) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean onQueryTextChange(String newText) {
 | 
			
		||||
                adapter.filter(getApplication().moduleMap, newText);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -124,91 +111,4 @@ public class ReposFragment extends Fragment implements CallbackEvent.Listener<Vo
 | 
			
		||||
        super.onDestroyView();
 | 
			
		||||
        unbinder.unbind();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void reloadRepos() {
 | 
			
		||||
        mUpdateRepos.clear();
 | 
			
		||||
        mInstalledRepos.clear();
 | 
			
		||||
        mOthersRepos.clear();
 | 
			
		||||
        for (Repo repo : getApplication().repoMap.values()) {
 | 
			
		||||
            Module module = getApplication().moduleMap.get(repo.getId());
 | 
			
		||||
            if (module != null) {
 | 
			
		||||
                if (repo.getVersionCode() > module.getVersionCode())
 | 
			
		||||
                    mUpdateRepos.add(repo);
 | 
			
		||||
                else
 | 
			
		||||
                    mInstalledRepos.add(repo);
 | 
			
		||||
            } else {
 | 
			
		||||
                mOthersRepos.add(repo);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        fUpdateRepos.clear();
 | 
			
		||||
        fInstalledRepos.clear();
 | 
			
		||||
        fOthersRepos.clear();
 | 
			
		||||
        fUpdateRepos.addAll(mUpdateRepos);
 | 
			
		||||
        fInstalledRepos.addAll(mInstalledRepos);
 | 
			
		||||
        fOthersRepos.addAll(mOthersRepos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateUI() {
 | 
			
		||||
        if (fUpdateRepos.size() + fInstalledRepos.size() + fOthersRepos.size() == 0) {
 | 
			
		||||
            emptyRv.setVisibility(View.VISIBLE);
 | 
			
		||||
            recyclerView.setVisibility(View.GONE);
 | 
			
		||||
        } else {
 | 
			
		||||
            List<SimpleSectionedRecyclerViewAdapter.Section> sections = new ArrayList<>();
 | 
			
		||||
            if (!fUpdateRepos.isEmpty()) {
 | 
			
		||||
                sections.add(new SimpleSectionedRecyclerViewAdapter.Section(0, getString(R.string.update_available)));
 | 
			
		||||
            }
 | 
			
		||||
            if (!fInstalledRepos.isEmpty()) {
 | 
			
		||||
                sections.add(new SimpleSectionedRecyclerViewAdapter.Section(fUpdateRepos.size(), getString(R.string.installed)));
 | 
			
		||||
            }
 | 
			
		||||
            if (!fOthersRepos.isEmpty()) {
 | 
			
		||||
                sections.add(new SimpleSectionedRecyclerViewAdapter.Section(fUpdateRepos.size() + fInstalledRepos.size(), getString(R.string.not_installed)));
 | 
			
		||||
            }
 | 
			
		||||
            SimpleSectionedRecyclerViewAdapter.Section[] array = sections.toArray(new SimpleSectionedRecyclerViewAdapter.Section[sections.size()]);
 | 
			
		||||
            mSectionedAdapter.setSections(array);
 | 
			
		||||
            emptyRv.setVisibility(View.GONE);
 | 
			
		||||
            recyclerView.setVisibility(View.VISIBLE);
 | 
			
		||||
        }
 | 
			
		||||
        mSwipeRefreshLayout.setRefreshing(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class FilterApps extends Async.NormalTask<String, Void, Void> {
 | 
			
		||||
        @Override
 | 
			
		||||
        protected Void doInBackground(String... strings) {
 | 
			
		||||
            String newText = strings[0];
 | 
			
		||||
            fUpdateRepos.clear();
 | 
			
		||||
            fInstalledRepos.clear();
 | 
			
		||||
            fOthersRepos.clear();
 | 
			
		||||
            for (Repo repo: mUpdateRepos) {
 | 
			
		||||
                if (repo.getName().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getAuthor().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getDescription().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        ) {
 | 
			
		||||
                    fUpdateRepos.add(repo);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            for (Repo repo: mInstalledRepos) {
 | 
			
		||||
                if (repo.getName().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getAuthor().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getDescription().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        ) {
 | 
			
		||||
                    fInstalledRepos.add(repo);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            for (Repo repo: mOthersRepos) {
 | 
			
		||||
                if (repo.getName().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getAuthor().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getDescription().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        ) {
 | 
			
		||||
                    fOthersRepos.add(repo);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void onPostExecute(Void v) {
 | 
			
		||||
            updateUI();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,49 +1,65 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.app.job.JobInfo;
 | 
			
		||||
import android.app.job.JobScheduler;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.LoadApps;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.LoadModules;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.LoadRepos;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Async;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.services.UpdateCheckService;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
public class SplashActivity extends Activity{
 | 
			
		||||
 | 
			
		||||
public class SplashActivity extends Activity {
 | 
			
		||||
    private static final int UPDATE_SERVICE_ID = 1;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
 | 
			
		||||
        MagiskManager magiskManager = getTopApplication();
 | 
			
		||||
        MagiskManager magiskManager = getApplicationContext();
 | 
			
		||||
 | 
			
		||||
        // Init the info and configs and root shell
 | 
			
		||||
        // Init the info and configs and root sh
 | 
			
		||||
        magiskManager.init();
 | 
			
		||||
 | 
			
		||||
        // Check MagiskHide status
 | 
			
		||||
        List<String> ret = Shell.sh("getprop persist.magisk.hide");
 | 
			
		||||
        boolean started = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
 | 
			
		||||
        // Get possible additional info from intent
 | 
			
		||||
        magiskManager.remoteMagiskVersionString = getIntent().getStringExtra(MagiskManager.INTENT_VERSION);
 | 
			
		||||
        magiskManager.magiskLink = getIntent().getStringExtra(MagiskManager.INTENT_LINK);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        LoadModules loadModuleTask = new LoadModules(this);
 | 
			
		||||
 | 
			
		||||
        if (Utils.checkNetworkStatus(this)) {
 | 
			
		||||
            // Initialize the update check service, notify every 8 hours
 | 
			
		||||
            if (!TextUtils.equals("install", getIntent().getStringExtra(MagiskManager.INTENT_SECTION))) {
 | 
			
		||||
                ComponentName service = new ComponentName(this, UpdateCheckService.class);
 | 
			
		||||
                JobInfo jobInfo = new JobInfo.Builder(UPDATE_SERVICE_ID, service)
 | 
			
		||||
                        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
 | 
			
		||||
                        .setPersisted(true)
 | 
			
		||||
                        .setPeriodic(8 * 60 * 60 * 1000)
 | 
			
		||||
                        .build();
 | 
			
		||||
                JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
 | 
			
		||||
                scheduler.schedule(jobInfo);
 | 
			
		||||
            }
 | 
			
		||||
            loadModuleTask.setCallBack(() -> new LoadRepos(this).exec());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Now fire all async tasks
 | 
			
		||||
        new Async.CheckUpdates(magiskManager).exec();
 | 
			
		||||
        new Async.GetBootBlocks(magiskManager).exec();
 | 
			
		||||
        if (magiskManager.magiskHide && !magiskManager.disabled &&
 | 
			
		||||
                magiskManager.magiskVersion > 11 && !started) {
 | 
			
		||||
            new Async.MagiskHide().enable();
 | 
			
		||||
        }
 | 
			
		||||
        new Async.LoadModules(magiskManager) {
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void onPostExecute(Void v) {
 | 
			
		||||
                super.onPostExecute(v);
 | 
			
		||||
                new Async.LoadRepos(magiskManager).exec();
 | 
			
		||||
            }
 | 
			
		||||
        }.exec();
 | 
			
		||||
        new Async.LoadApps(magiskManager).exec();
 | 
			
		||||
        loadModuleTask.exec();
 | 
			
		||||
        new LoadApps(this).exec();
 | 
			
		||||
 | 
			
		||||
        // Preparation done, now start main activity
 | 
			
		||||
        Intent intent = new Intent(getApplicationContext(), MainActivity.class);
 | 
			
		||||
        Intent intent = new Intent(this, MainActivity.class);
 | 
			
		||||
        String section = getIntent().getStringExtra(MagiskManager.INTENT_SECTION);
 | 
			
		||||
        if (section != null) {
 | 
			
		||||
            intent.putExtra(MagiskManager.INTENT_SECTION, section);
 | 
			
		||||
        }
 | 
			
		||||
        startActivity(intent);
 | 
			
		||||
        finish();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -13,7 +13,6 @@ import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.adapters.SuLogAdapter;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.superuser.SuLogDatabaseHelper;
 | 
			
		||||
import com.topjohnwu.magisk.superuser.SuLogEntry;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -28,7 +27,7 @@ public class SuLogFragment extends Fragment {
 | 
			
		||||
    @BindView(R.id.recyclerView) RecyclerView recyclerView;
 | 
			
		||||
 | 
			
		||||
    private Unbinder unbinder;
 | 
			
		||||
    private SuLogDatabaseHelper dbHelper;
 | 
			
		||||
    private MagiskManager magiskManager;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate(@Nullable Bundle savedInstanceState) {
 | 
			
		||||
@@ -48,8 +47,7 @@ public class SuLogFragment extends Fragment {
 | 
			
		||||
        // Inflate the layout for this fragment
 | 
			
		||||
        View v = inflater.inflate(R.layout.fragment_su_log, container, false);
 | 
			
		||||
        unbinder = ButterKnife.bind(this, v);
 | 
			
		||||
 | 
			
		||||
        dbHelper = new SuLogDatabaseHelper(getActivity());
 | 
			
		||||
        magiskManager = getApplication();
 | 
			
		||||
 | 
			
		||||
        updateList();
 | 
			
		||||
 | 
			
		||||
@@ -57,7 +55,7 @@ public class SuLogFragment extends Fragment {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateList() {
 | 
			
		||||
        List<SuLogEntry> logs = dbHelper.getLogList();
 | 
			
		||||
        List<SuLogEntry> logs = magiskManager.suDB.getLogList();
 | 
			
		||||
 | 
			
		||||
        if (logs.size() == 0) {
 | 
			
		||||
            emptyRv.setVisibility(View.VISIBLE);
 | 
			
		||||
@@ -76,7 +74,7 @@ public class SuLogFragment extends Fragment {
 | 
			
		||||
                updateList();
 | 
			
		||||
                return true;
 | 
			
		||||
            case R.id.menu_clear:
 | 
			
		||||
                dbHelper.clearLogs();
 | 
			
		||||
                magiskManager.suDB.clearLogs();
 | 
			
		||||
                updateList();
 | 
			
		||||
                return true;
 | 
			
		||||
            default:
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ import android.widget.TextView;
 | 
			
		||||
import com.topjohnwu.magisk.adapters.PolicyAdapter;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.superuser.Policy;
 | 
			
		||||
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -33,15 +32,15 @@ public class SuperuserFragment extends Fragment {
 | 
			
		||||
        unbinder = ButterKnife.bind(this, view);
 | 
			
		||||
 | 
			
		||||
        PackageManager pm = getActivity().getPackageManager();
 | 
			
		||||
        MagiskManager magiskManager = getApplication();
 | 
			
		||||
 | 
			
		||||
        SuDatabaseHelper dbHelper = new SuDatabaseHelper(getActivity());
 | 
			
		||||
        List<Policy> policyList = dbHelper.getPolicyList(pm);
 | 
			
		||||
        List<Policy> policyList = magiskManager.suDB.getPolicyList(pm);
 | 
			
		||||
 | 
			
		||||
        if (policyList.size() == 0) {
 | 
			
		||||
            emptyRv.setVisibility(View.VISIBLE);
 | 
			
		||||
            recyclerView.setVisibility(View.GONE);
 | 
			
		||||
        } else {
 | 
			
		||||
            recyclerView.setAdapter(new PolicyAdapter(policyList, dbHelper, pm));
 | 
			
		||||
            recyclerView.setAdapter(new PolicyAdapter(policyList, magiskManager.suDB, pm));
 | 
			
		||||
            emptyRv.setVisibility(View.GONE);
 | 
			
		||||
            recyclerView.setVisibility(View.VISIBLE);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package com.topjohnwu.magisk.adapters;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.content.pm.ApplicationInfo;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.support.design.widget.Snackbar;
 | 
			
		||||
@@ -13,7 +14,8 @@ import android.widget.ImageView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Async;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.MagiskHide;
 | 
			
		||||
import com.topjohnwu.magisk.components.SnackbarMaker;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
@@ -50,8 +52,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setLists(List<ApplicationInfo> listApps, List<String> hideList) {
 | 
			
		||||
        mOriginalList = mList = Collections.unmodifiableList(listApps);
 | 
			
		||||
        mHideList = new ArrayList<>(hideList);
 | 
			
		||||
        mOriginalList = mList = listApps;
 | 
			
		||||
        mHideList = hideList;
 | 
			
		||||
        notifyDataSetChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -76,20 +78,19 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
 | 
			
		||||
        if (SNLIST.contains(info.packageName)) {
 | 
			
		||||
            holder.checkBox.setChecked(true);
 | 
			
		||||
            holder.checkBox.setEnabled(false);
 | 
			
		||||
            holder.itemView.setOnClickListener(v -> {
 | 
			
		||||
                Snackbar snackbar = Snackbar.make(holder.itemView, R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG);
 | 
			
		||||
                ((TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text)).setMaxLines(2);
 | 
			
		||||
                snackbar.show();
 | 
			
		||||
            });
 | 
			
		||||
            holder.itemView.setOnClickListener(v ->
 | 
			
		||||
                SnackbarMaker.make(holder.itemView,
 | 
			
		||||
                        R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG).show()
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            holder.checkBox.setEnabled(true);
 | 
			
		||||
            holder.checkBox.setChecked(mHideList.contains(info.packageName));
 | 
			
		||||
            holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
 | 
			
		||||
                if (isChecked) {
 | 
			
		||||
                    new Async.MagiskHide().add(info.packageName);
 | 
			
		||||
                    new MagiskHide((Activity) holder.itemView.getContext()).add(info.packageName);
 | 
			
		||||
                    mHideList.add(info.packageName);
 | 
			
		||||
                } else {
 | 
			
		||||
                    new Async.MagiskHide().rm(info.packageName);
 | 
			
		||||
                    new MagiskHide((Activity) holder.itemView.getContext()).rm(info.packageName);
 | 
			
		||||
                    mHideList.remove(info.packageName);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,8 @@ import android.widget.ImageView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.components.SnackbarMaker;
 | 
			
		||||
import com.topjohnwu.magisk.module.Module;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Async;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -38,6 +38,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBindViewHolder(final ViewHolder holder, int position) {
 | 
			
		||||
        Context context = holder.itemView.getContext();
 | 
			
		||||
        Shell rootShell = Shell.getShell(context);
 | 
			
		||||
        final Module module = mList.get(position);
 | 
			
		||||
 | 
			
		||||
        String version = module.getVersion();
 | 
			
		||||
@@ -52,44 +53,31 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
 | 
			
		||||
 | 
			
		||||
        holder.checkBox.setOnCheckedChangeListener(null);
 | 
			
		||||
        holder.checkBox.setChecked(module.isEnabled());
 | 
			
		||||
        holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> new Async.RootTask<Void, Void, Void>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            protected Void doInBackground(Void... voids) {
 | 
			
		||||
                if (isChecked) {
 | 
			
		||||
                    module.removeDisableFile();
 | 
			
		||||
                } else {
 | 
			
		||||
                    module.createDisableFile();
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
        holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
 | 
			
		||||
            int snack;
 | 
			
		||||
            if (isChecked) {
 | 
			
		||||
                module.removeDisableFile(rootShell);
 | 
			
		||||
                snack = R.string.disable_file_removed;
 | 
			
		||||
            } else {
 | 
			
		||||
                module.createDisableFile(rootShell);
 | 
			
		||||
                snack = R.string.disable_file_created;
 | 
			
		||||
            }
 | 
			
		||||
            SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void onPostExecute(Void v) {
 | 
			
		||||
                int snack = isChecked ? R.string.disable_file_removed : R.string.disable_file_created;
 | 
			
		||||
                Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
        holder.delete.setOnClickListener(v -> {
 | 
			
		||||
            boolean removed = module.willBeRemoved();
 | 
			
		||||
            int snack;
 | 
			
		||||
            if (removed) {
 | 
			
		||||
                module.deleteRemoveFile(rootShell);
 | 
			
		||||
                snack = R.string.remove_file_deleted;
 | 
			
		||||
            } else {
 | 
			
		||||
                module.createRemoveFile(rootShell);
 | 
			
		||||
                snack = R.string.remove_file_created;
 | 
			
		||||
            }
 | 
			
		||||
        }.exec());
 | 
			
		||||
 | 
			
		||||
        holder.delete.setOnClickListener(v -> new Async.RootTask<Void, Void, Void>() {
 | 
			
		||||
            private final boolean removed = module.willBeRemoved();
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected Void doInBackground(Void... voids) {
 | 
			
		||||
                if (removed) {
 | 
			
		||||
                    module.deleteRemoveFile();
 | 
			
		||||
                } else {
 | 
			
		||||
                    module.createRemoveFile();
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void onPostExecute(Void v) {
 | 
			
		||||
                int snack = removed ? R.string.remove_file_deleted : R.string.remove_file_created;
 | 
			
		||||
                Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
                updateDeleteButton(holder, module);
 | 
			
		||||
            }
 | 
			
		||||
        }.exec());
 | 
			
		||||
            SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
            updateDeleteButton(holder, module);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (module.isUpdated()) {
 | 
			
		||||
            holder.notice.setVisibility(View.VISIBLE);
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,178 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.adapters;
 | 
			
		||||
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.util.SparseArray;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
 | 
			
		||||
public class SimpleSectionedRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 | 
			
		||||
 | 
			
		||||
    private static final int SECTION_TYPE = 0;
 | 
			
		||||
 | 
			
		||||
    private boolean mValid = true;
 | 
			
		||||
    private int mSectionResourceId;
 | 
			
		||||
    private int mTextResourceId;
 | 
			
		||||
    private RecyclerView.Adapter mBaseAdapter;
 | 
			
		||||
    private SparseArray<Section> mSections = new SparseArray<Section>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public SimpleSectionedRecyclerViewAdapter(int sectionResourceId, int textResourceId,
 | 
			
		||||
                                              RecyclerView.Adapter baseAdapter) {
 | 
			
		||||
 | 
			
		||||
        mSectionResourceId = sectionResourceId;
 | 
			
		||||
        mTextResourceId = textResourceId;
 | 
			
		||||
        mBaseAdapter = baseAdapter;
 | 
			
		||||
 | 
			
		||||
        mBaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onChanged() {
 | 
			
		||||
                mValid = mBaseAdapter.getItemCount()>0;
 | 
			
		||||
                notifyDataSetChanged();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onItemRangeChanged(int positionStart, int itemCount) {
 | 
			
		||||
                mValid = mBaseAdapter.getItemCount()>0;
 | 
			
		||||
                notifyItemRangeChanged(positionStart, itemCount);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onItemRangeInserted(int positionStart, int itemCount) {
 | 
			
		||||
                mValid = mBaseAdapter.getItemCount()>0;
 | 
			
		||||
                notifyItemRangeInserted(positionStart, itemCount);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onItemRangeRemoved(int positionStart, int itemCount) {
 | 
			
		||||
                mValid = mBaseAdapter.getItemCount()>0;
 | 
			
		||||
                notifyItemRangeRemoved(positionStart, itemCount);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static class SectionViewHolder extends RecyclerView.ViewHolder {
 | 
			
		||||
 | 
			
		||||
        public TextView title;
 | 
			
		||||
 | 
			
		||||
        public SectionViewHolder(View view, int mTextResourceid) {
 | 
			
		||||
            super(view);
 | 
			
		||||
            title = (TextView) view.findViewById(mTextResourceid);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) {
 | 
			
		||||
        if (typeView == SECTION_TYPE) {
 | 
			
		||||
            View view = LayoutInflater.from(parent.getContext()).inflate(mSectionResourceId, parent, false);
 | 
			
		||||
            return new SectionViewHolder(view,mTextResourceId);
 | 
			
		||||
        }else{
 | 
			
		||||
            return mBaseAdapter.onCreateViewHolder(parent, typeView -1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBindViewHolder(RecyclerView.ViewHolder sectionViewHolder, int position) {
 | 
			
		||||
        if (isSectionHeaderPosition(position)) {
 | 
			
		||||
            ((SectionViewHolder)sectionViewHolder).title.setText(mSections.get(position).title);
 | 
			
		||||
        }else{
 | 
			
		||||
            mBaseAdapter.onBindViewHolder(sectionViewHolder,sectionedPositionToPosition(position));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getItemViewType(int position) {
 | 
			
		||||
        return isSectionHeaderPosition(position)
 | 
			
		||||
                ? SECTION_TYPE
 | 
			
		||||
                : mBaseAdapter.getItemViewType(sectionedPositionToPosition(position)) +1 ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static class Section {
 | 
			
		||||
        int firstPosition;
 | 
			
		||||
        int sectionedPosition;
 | 
			
		||||
        CharSequence title;
 | 
			
		||||
 | 
			
		||||
        public Section(int firstPosition, CharSequence title) {
 | 
			
		||||
            this.firstPosition = firstPosition;
 | 
			
		||||
            this.title = title;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public CharSequence getTitle() {
 | 
			
		||||
            return title;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void setSections(Section[] sections) {
 | 
			
		||||
        mSections.clear();
 | 
			
		||||
 | 
			
		||||
        Arrays.sort(sections, new Comparator<Section>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public int compare(Section o, Section o1) {
 | 
			
		||||
                return (o.firstPosition == o1.firstPosition)
 | 
			
		||||
                        ? 0
 | 
			
		||||
                        : ((o.firstPosition < o1.firstPosition) ? -1 : 1);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        int offset = 0; // offset positions for the headers we're adding
 | 
			
		||||
        for (Section section : sections) {
 | 
			
		||||
            section.sectionedPosition = section.firstPosition + offset;
 | 
			
		||||
            mSections.append(section.sectionedPosition, section);
 | 
			
		||||
            ++offset;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        notifyDataSetChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int positionToSectionedPosition(int position) {
 | 
			
		||||
        int offset = 0;
 | 
			
		||||
        for (int i = 0; i < mSections.size(); i++) {
 | 
			
		||||
            if (mSections.valueAt(i).firstPosition > position) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            ++offset;
 | 
			
		||||
        }
 | 
			
		||||
        return position + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int sectionedPositionToPosition(int sectionedPosition) {
 | 
			
		||||
        if (isSectionHeaderPosition(sectionedPosition)) {
 | 
			
		||||
            return RecyclerView.NO_POSITION;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int offset = 0;
 | 
			
		||||
        for (int i = 0; i < mSections.size(); i++) {
 | 
			
		||||
            if (mSections.valueAt(i).sectionedPosition > sectionedPosition) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            --offset;
 | 
			
		||||
        }
 | 
			
		||||
        return sectionedPosition + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isSectionHeaderPosition(int position) {
 | 
			
		||||
        return mSections.get(position) != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long getItemId(int position) {
 | 
			
		||||
        return isSectionHeaderPosition(position)
 | 
			
		||||
                ? Integer.MAX_VALUE - mSections.indexOfKey(position)
 | 
			
		||||
                : mBaseAdapter.getItemId(sectionedPositionToPosition(position));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getItemCount() {
 | 
			
		||||
        return (mValid ? mBaseAdapter.getItemCount() + mSections.size() : 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +1,13 @@
 | 
			
		||||
package com.topjohnwu.magisk.adapters;
 | 
			
		||||
 | 
			
		||||
import android.animation.Animator;
 | 
			
		||||
import android.animation.ValueAnimator;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.view.ViewTreeObserver;
 | 
			
		||||
import android.view.animation.Animation;
 | 
			
		||||
import android.view.animation.RotateAnimation;
 | 
			
		||||
import android.widget.ImageView;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.thoughtbot.expandablerecyclerview.ExpandableRecyclerViewAdapter;
 | 
			
		||||
@@ -19,6 +15,7 @@ import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
 | 
			
		||||
import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
 | 
			
		||||
import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.components.ExpandableViewHolder;
 | 
			
		||||
import com.topjohnwu.magisk.superuser.SuLogEntry;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
@@ -90,7 +87,7 @@ public class SuLogAdapter {
 | 
			
		||||
            SuLogEntry logEntry = (SuLogEntry) group.getItems().get(childIndex);
 | 
			
		||||
            holder.setExpanded(expandList.contains(logEntry));
 | 
			
		||||
            holder.itemView.setOnClickListener(view -> {
 | 
			
		||||
                if (holder.mExpanded) {
 | 
			
		||||
                if (holder.getExpanded()) {
 | 
			
		||||
                    holder.collapse();
 | 
			
		||||
                    expandList.remove(logEntry);
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -109,7 +106,9 @@ public class SuLogAdapter {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onBindGroupViewHolder(LogGroupViewHolder holder, int flatPosition, ExpandableGroup group) {
 | 
			
		||||
            holder.date.setText(group.getTitle());
 | 
			
		||||
            if (isGroupExpanded(flatPosition)) holder.expand();
 | 
			
		||||
            if (isGroupExpanded(flatPosition)) {
 | 
			
		||||
                holder.expand();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -148,92 +147,50 @@ public class SuLogAdapter {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Wrapper class
 | 
			
		||||
    static class LogViewHolder extends ChildViewHolder {
 | 
			
		||||
 | 
			
		||||
        private InternalViewHolder expandableViewHolder;
 | 
			
		||||
 | 
			
		||||
        @BindView(R.id.app_name) TextView appName;
 | 
			
		||||
        @BindView(R.id.action) TextView action;
 | 
			
		||||
        @BindView(R.id.time) TextView time;
 | 
			
		||||
        @BindView(R.id.fromPid) TextView fromPid;
 | 
			
		||||
        @BindView(R.id.toUid) TextView toUid;
 | 
			
		||||
        @BindView(R.id.command) TextView command;
 | 
			
		||||
        @BindView(R.id.expand_layout) LinearLayout expandLayout;
 | 
			
		||||
 | 
			
		||||
        private ValueAnimator mAnimator;
 | 
			
		||||
        private boolean mExpanded = false;
 | 
			
		||||
        private static int expandHeight = 0;
 | 
			
		||||
 | 
			
		||||
        public LogViewHolder(View itemView) {
 | 
			
		||||
        LogViewHolder(View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
            ButterKnife.bind(this, itemView);
 | 
			
		||||
            expandLayout.getViewTreeObserver().addOnPreDrawListener(
 | 
			
		||||
                    new ViewTreeObserver.OnPreDrawListener() {
 | 
			
		||||
            expandableViewHolder = new InternalViewHolder(itemView);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public boolean onPreDraw() {
 | 
			
		||||
                            if (expandHeight == 0) {
 | 
			
		||||
                                final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
 | 
			
		||||
                                final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
 | 
			
		||||
                                expandLayout.measure(widthSpec, heightSpec);
 | 
			
		||||
                                expandHeight = expandLayout.getMeasuredHeight();
 | 
			
		||||
                            }
 | 
			
		||||
        private class InternalViewHolder extends ExpandableViewHolder {
 | 
			
		||||
 | 
			
		||||
                            expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
 | 
			
		||||
                            expandLayout.setVisibility(View.GONE);
 | 
			
		||||
                            mAnimator = slideAnimator(0, expandHeight);
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
            InternalViewHolder(View itemView) {
 | 
			
		||||
                super(itemView);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                    });
 | 
			
		||||
            @Override
 | 
			
		||||
            public void setExpandLayout(View itemView) {
 | 
			
		||||
                expandLayout = itemView.findViewById(R.id.expand_layout);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private boolean getExpanded() {
 | 
			
		||||
            return expandableViewHolder.mExpanded;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void setExpanded(boolean expanded) {
 | 
			
		||||
            mExpanded = expanded;
 | 
			
		||||
            ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
 | 
			
		||||
            layoutParams.height = expanded ? expandHeight : 0;
 | 
			
		||||
            expandLayout.setLayoutParams(layoutParams);
 | 
			
		||||
            expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE);
 | 
			
		||||
            expandableViewHolder.setExpanded(expanded);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void expand() {
 | 
			
		||||
            expandLayout.setVisibility(View.VISIBLE);
 | 
			
		||||
            mAnimator.start();
 | 
			
		||||
            mExpanded = true;
 | 
			
		||||
            expandableViewHolder.expand();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void collapse() {
 | 
			
		||||
            if (!mExpanded) return;
 | 
			
		||||
            int finalHeight = expandLayout.getHeight();
 | 
			
		||||
            ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
 | 
			
		||||
            mAnimator.addListener(new Animator.AnimatorListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationEnd(Animator animator) {
 | 
			
		||||
                    expandLayout.setVisibility(View.GONE);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationStart(Animator animator) {}
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationCancel(Animator animator) {}
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationRepeat(Animator animator) {}
 | 
			
		||||
            });
 | 
			
		||||
            mAnimator.start();
 | 
			
		||||
            mExpanded = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private ValueAnimator slideAnimator(int start, int end) {
 | 
			
		||||
 | 
			
		||||
            ValueAnimator animator = ValueAnimator.ofInt(start, end);
 | 
			
		||||
 | 
			
		||||
            animator.addUpdateListener(valueAnimator -> {
 | 
			
		||||
                int value = (Integer) valueAnimator.getAnimatedValue();
 | 
			
		||||
                ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
 | 
			
		||||
                layoutParams.height = value;
 | 
			
		||||
                expandLayout.setLayoutParams(layoutParams);
 | 
			
		||||
            });
 | 
			
		||||
            return animator;
 | 
			
		||||
            expandableViewHolder.collapse();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,62 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.BuildConfig;
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.WebService;
 | 
			
		||||
 | 
			
		||||
import org.json.JSONException;
 | 
			
		||||
import org.json.JSONObject;
 | 
			
		||||
 | 
			
		||||
public class CheckUpdates extends ParallelTask<Void, Void, Void> {
 | 
			
		||||
 | 
			
		||||
    private static final String UPDATE_JSON = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/update/magisk_update.json";
 | 
			
		||||
 | 
			
		||||
    private boolean showNotification = false;
 | 
			
		||||
 | 
			
		||||
    public CheckUpdates(Context context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CheckUpdates(Context context, boolean b) {
 | 
			
		||||
        super(context);
 | 
			
		||||
        showNotification = b;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Void doInBackground(Void... voids) {
 | 
			
		||||
        MagiskManager magiskManager = getMagiskManager();
 | 
			
		||||
        if (magiskManager == null) return null;
 | 
			
		||||
        String jsonStr = WebService.request(UPDATE_JSON, WebService.GET);
 | 
			
		||||
        try {
 | 
			
		||||
            JSONObject json = new JSONObject(jsonStr);
 | 
			
		||||
            JSONObject magisk = json.getJSONObject("magisk");
 | 
			
		||||
            magiskManager.remoteMagiskVersionString = magisk.getString("version");
 | 
			
		||||
            magiskManager.remoteMagiskVersionCode = magisk.getInt("versionCode");
 | 
			
		||||
            magiskManager.magiskLink = magisk.getString("link");
 | 
			
		||||
            magiskManager.releaseNoteLink = magisk.getString("note");
 | 
			
		||||
            JSONObject manager = json.getJSONObject("app");
 | 
			
		||||
            magiskManager.remoteManagerVersionString = manager.getString("version");
 | 
			
		||||
            magiskManager.remoteManagerVersionCode = manager.getInt("versionCode");
 | 
			
		||||
            magiskManager.managerLink = manager.getString("link");
 | 
			
		||||
        } catch (JSONException ignored) {}
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Void v) {
 | 
			
		||||
        MagiskManager magiskManager = getMagiskManager();
 | 
			
		||||
        if (magiskManager == null) return;
 | 
			
		||||
        if (showNotification && magiskManager.updateNotification) {
 | 
			
		||||
            if (BuildConfig.VERSION_CODE < magiskManager.remoteManagerVersionCode) {
 | 
			
		||||
                Utils.showManagerUpdate(magiskManager);
 | 
			
		||||
            } else if (magiskManager.magiskVersionCode < magiskManager.remoteMagiskVersionCode) {
 | 
			
		||||
                Utils.showMagiskUpdate(magiskManager);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        magiskManager.updateCheckDone.trigger();
 | 
			
		||||
        super.onPostExecute(v);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user