mirror of
https://github.com/topjohnwu/Magisk
synced 2025-11-01 12:30:51 +01:00
Compare commits
126 Commits
v19.1
...
manager-v7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fe03d2795 | ||
|
|
c595a87ccf | ||
|
|
fac07c3913 | ||
|
|
c63fdbbc6b | ||
|
|
2ff5d9606b | ||
|
|
ed43452c1a | ||
|
|
8f28d4028f | ||
|
|
b54543b18c | ||
|
|
966d6593ca | ||
|
|
ad95b1c9d1 | ||
|
|
3bfa38c60a | ||
|
|
0bdbcad8be | ||
|
|
80cd85b061 | ||
|
|
89275270f3 | ||
|
|
e7339ba619 | ||
|
|
d9ad7d522c | ||
|
|
ef0e22cc41 | ||
|
|
62db65bf18 | ||
|
|
d5371f752c | ||
|
|
a5f5e94115 | ||
|
|
67c3f40adb | ||
|
|
ff7a0ba599 | ||
|
|
b152c63102 | ||
|
|
415ff23be5 | ||
|
|
b0d6de783e | ||
|
|
ac28e6e5ca | ||
|
|
a9350f50c9 | ||
|
|
ed7babcbf1 | ||
|
|
61ebc335c4 | ||
|
|
0167bd76f1 | ||
|
|
79d704008b | ||
|
|
0a703585b0 | ||
|
|
b27801a27c | ||
|
|
a0cfce7cbc | ||
|
|
8b7144c986 | ||
|
|
d3f5f5ee59 | ||
|
|
a2a3c7f438 | ||
|
|
4496f82d5b | ||
|
|
09d531557d | ||
|
|
7fee82f731 | ||
|
|
475054c48a | ||
|
|
a743d05751 | ||
|
|
d1ed502e03 | ||
|
|
37744c7ab6 | ||
|
|
b25c49725f | ||
|
|
b245782c7e | ||
|
|
a9f32baae0 | ||
|
|
e7ef71865d | ||
|
|
88c4f72b37 | ||
|
|
abbcdf91a5 | ||
|
|
b876df6e21 | ||
|
|
4bb81f35d7 | ||
|
|
ff20267b3f | ||
|
|
2c9586d811 | ||
|
|
2813d2031a | ||
|
|
db218407b0 | ||
|
|
d52210dd90 | ||
|
|
f3cd9a096a | ||
|
|
e426090a18 | ||
|
|
cbe64fd559 | ||
|
|
63ea7a70bd | ||
|
|
fb0998f7a2 | ||
|
|
a9b00dd537 | ||
|
|
52eb059515 | ||
|
|
7640246255 | ||
|
|
52c83b2916 | ||
|
|
d9cded0fc9 | ||
|
|
750c42caf1 | ||
|
|
bbf650c6cf | ||
|
|
a25dace7e0 | ||
|
|
14ff22fbcd | ||
|
|
07eb7dda2d | ||
|
|
d4058175b4 | ||
|
|
2de984ae24 | ||
|
|
761a8bf2a9 | ||
|
|
6df7006b36 | ||
|
|
aceb3ee863 | ||
|
|
11d716a3c8 | ||
|
|
7cc8c014eb | ||
|
|
f21241d944 | ||
|
|
a181fa0652 | ||
|
|
3f748b4d2a | ||
|
|
683450f9c6 | ||
|
|
adbd47a36c | ||
|
|
ce693aa5e9 | ||
|
|
ad80804461 | ||
|
|
2d55632430 | ||
|
|
e81f00ef1a | ||
|
|
93fb0e3d74 | ||
|
|
71ce0de606 | ||
|
|
0407062c1d | ||
|
|
cda14af208 | ||
|
|
258f170cd7 | ||
|
|
f76015d714 | ||
|
|
7e5e14163c | ||
|
|
bcd1064e94 | ||
|
|
8a8441c875 | ||
|
|
15aa813416 | ||
|
|
605faccffd | ||
|
|
79f2d08c81 | ||
|
|
0568ae5391 | ||
|
|
5330dda9f8 | ||
|
|
ebab126579 | ||
|
|
0e5417a13e | ||
|
|
9a968e0584 | ||
|
|
ffec64d209 | ||
|
|
f332746188 | ||
|
|
b2fa5b551e | ||
|
|
36e83edddc | ||
|
|
6b045eadef | ||
|
|
147264822c | ||
|
|
36e4ccd800 | ||
|
|
796c16237d | ||
|
|
861ad9881c | ||
|
|
3101c538e9 | ||
|
|
42adc7382f | ||
|
|
9bb4dfad13 | ||
|
|
bd00ae8ede | ||
|
|
f309522268 | ||
|
|
0efaddff23 | ||
|
|
94ba7cb0c5 | ||
|
|
2d58c725e0 | ||
|
|
e035523eb8 | ||
|
|
bea5308ab7 | ||
|
|
f006a85fec | ||
|
|
ea93013ebc |
@@ -1,6 +1,6 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
kapt {
|
||||
@@ -16,6 +16,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId 'com.topjohnwu.magisk'
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
multiDexEnabled true
|
||||
versionName configProps['appVersion']
|
||||
versionCode configProps['appVersionCode'] as Integer
|
||||
javaCompileOptions {
|
||||
@@ -30,6 +31,20 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
dataBinding {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude '/META-INF/*.version'
|
||||
exclude '/META-INF/*.kotlin_module'
|
||||
exclude '/META-INF/rxkotlin.properties'
|
||||
exclude '/androidsupportmultidexversion.txt'
|
||||
exclude '/org/**'
|
||||
exclude '/kotlin/**'
|
||||
exclude '/kotlinx/**'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -37,30 +52,32 @@ dependencies {
|
||||
implementation project(':net')
|
||||
implementation project(':shared')
|
||||
implementation project(':signing')
|
||||
implementation 'com.github.topjohnwu:jtar:1.0.0'
|
||||
implementation 'net.sourceforge.streamsupport:android-retrostreams:1.7.0'
|
||||
implementation 'com.github.sevar83:indeterminate-checkbox:1.0.5'
|
||||
|
||||
def markwonVersion = '3.0.0'
|
||||
implementation 'com.github.topjohnwu:jtar:1.0.0'
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.github.skoumalcz:teanity:0.3.3'
|
||||
implementation 'com.ncapdevi:frag-nav:3.2.0'
|
||||
|
||||
def markwonVersion = '3.0.1'
|
||||
implementation "ru.noties.markwon:core:${markwonVersion}"
|
||||
implementation "ru.noties.markwon:html:${markwonVersion}"
|
||||
implementation "ru.noties.markwon:image-svg:${markwonVersion}"
|
||||
|
||||
def androidXVersion = "1.0.0"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
implementation "androidx.preference:preference:${androidXVersion}"
|
||||
implementation "androidx.recyclerview:recyclerview:${androidXVersion}"
|
||||
implementation "androidx.cardview:cardview:${androidXVersion}"
|
||||
implementation "com.google.android.material:material:${androidXVersion}"
|
||||
implementation 'androidx.work:work-runtime:2.0.1'
|
||||
implementation 'androidx.transition:transition:1.1.0-beta01'
|
||||
|
||||
def libsuVersion = '2.5.0'
|
||||
implementation "com.github.topjohnwu.libsu:core:${libsuVersion}"
|
||||
implementation "com.github.topjohnwu.libsu:io:${libsuVersion}"
|
||||
|
||||
def butterKnifeVersion = '10.1.0'
|
||||
implementation "com.jakewharton:butterknife-runtime:${butterKnifeVersion}"
|
||||
kapt "com.jakewharton:butterknife-compiler:${butterKnifeVersion}"
|
||||
def koin = "2.0.0-rc-2"
|
||||
implementation "org.koin:koin-core:${koin}"
|
||||
implementation "org.koin:koin-android:${koin}"
|
||||
implementation "org.koin:koin-androidx-viewmodel:${koin}"
|
||||
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.preference:preference:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha05'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha06'
|
||||
implementation 'androidx.work:work-runtime:2.0.1'
|
||||
implementation 'androidx.transition:transition:1.2.0-alpha01'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
}
|
||||
|
||||
17
app/proguard-rules.pro
vendored
17
app/proguard-rules.pro
vendored
@@ -16,12 +16,6 @@
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# BouncyCastle
|
||||
-keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
|
||||
-keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
|
||||
-keep,allowoptimization class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
|
||||
-dontwarn javax.naming.**
|
||||
|
||||
# Snet
|
||||
-keepclassmembers class com.topjohnwu.magisk.utils.ISafetyNetHelper { *; }
|
||||
-keep,allowobfuscation interface com.topjohnwu.magisk.utils.ISafetyNetHelper$Callback
|
||||
@@ -29,18 +23,17 @@
|
||||
void onResponse(int);
|
||||
}
|
||||
|
||||
# Keep all fragment constructors
|
||||
-keepclassmembers class * extends androidx.fragment.app.Fragment {
|
||||
public <init>(...);
|
||||
}
|
||||
|
||||
# DelegateWorker
|
||||
-keep,allowobfuscation class * extends com.topjohnwu.magisk.model.worker.DelegateWorker
|
||||
|
||||
# BootSigner
|
||||
-keepclassmembers class com.topjohnwu.signing.BootSigner { *; }
|
||||
|
||||
# SVG
|
||||
-dontwarn com.caverock.androidsvg.SVGAndroidRenderer
|
||||
|
||||
# RetroStreams
|
||||
-dontwarn java9.**
|
||||
|
||||
# Strip logging
|
||||
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
|
||||
public *** debug(...);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:name="a.e"
|
||||
android:theme="@style/AppTheme"
|
||||
android:theme="@style/MagiskTheme"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
android:name="a.f"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:screenOrientation="nosensor"
|
||||
android:theme="@style/AppTheme.NoDrawer" />
|
||||
android:theme="@style/MagiskTheme.Flashing" />
|
||||
|
||||
<!-- Superuser -->
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
android:exported="false"
|
||||
android:directBootAware="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:theme="@style/SuRequest" />
|
||||
android:theme="@style/MagiskTheme.SU" />
|
||||
|
||||
<!-- Receiver -->
|
||||
|
||||
@@ -74,4 +74,4 @@
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import com.topjohnwu.magisk.data.database.MagiskDB;
|
||||
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper;
|
||||
import com.topjohnwu.magisk.ui.base.BaseActivity;
|
||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||
import com.topjohnwu.magisk.utils.RootUtils;
|
||||
import com.topjohnwu.net.Networking;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
public class App extends Application implements Application.ActivityLifecycleCallbacks {
|
||||
|
||||
public static App self;
|
||||
public static Context deContext;
|
||||
public static ThreadPoolExecutor THREAD_POOL;
|
||||
|
||||
// Global resources
|
||||
public SharedPreferences prefs;
|
||||
public MagiskDB mDB;
|
||||
public RepoDatabaseHelper repoDB;
|
||||
private volatile BaseActivity foreground;
|
||||
|
||||
static {
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER | Shell.FLAG_USE_MAGISK_BUSYBOX);
|
||||
Shell.Config.verboseLogging(BuildConfig.DEBUG);
|
||||
Shell.Config.addInitializers(RootUtils.class);
|
||||
Shell.Config.setTimeout(2);
|
||||
THREAD_POOL = (ThreadPoolExecutor) AsyncTask.THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
self = this;
|
||||
deContext = base;
|
||||
registerActivityLifecycleCallbacks(this);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
deContext = base.createDeviceProtectedStorageContext();
|
||||
deContext.moveSharedPreferencesFrom(base,
|
||||
PreferenceManager.getDefaultSharedPreferencesName(base));
|
||||
}
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(deContext);
|
||||
mDB = new MagiskDB(base);
|
||||
|
||||
Networking.init(base);
|
||||
LocaleManager.setLocale(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
LocaleManager.setLocale(this);
|
||||
}
|
||||
|
||||
public static BaseActivity foreground() {
|
||||
return self.foreground;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) {}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(@NonNull Activity activity) {}
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityResumed(@NonNull Activity activity) {
|
||||
foreground = (BaseActivity) activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onActivityPaused(@NonNull Activity activity) {
|
||||
foreground = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(@NonNull Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) {}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {}
|
||||
}
|
||||
130
app/src/main/java/com/topjohnwu/magisk/App.kt
Normal file
130
app/src/main/java/com/topjohnwu/magisk/App.kt
Normal file
@@ -0,0 +1,130 @@
|
||||
package com.topjohnwu.magisk
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Configuration
|
||||
import android.os.AsyncTask
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.multidex.MultiDex
|
||||
import com.topjohnwu.magisk.data.database.MagiskDB
|
||||
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper
|
||||
import com.topjohnwu.magisk.di.koinModules
|
||||
import com.topjohnwu.magisk.utils.LocaleManager
|
||||
import com.topjohnwu.magisk.utils.RootUtils
|
||||
import com.topjohnwu.magisk.utils.inject
|
||||
import com.topjohnwu.net.Networking
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ThreadPoolExecutor
|
||||
|
||||
open class App : Application(), Application.ActivityLifecycleCallbacks {
|
||||
|
||||
lateinit var protectedContext: Context
|
||||
|
||||
@Deprecated("Use dependency injection")
|
||||
val prefs: SharedPreferences by inject()
|
||||
@Deprecated("Use dependency injection")
|
||||
val DB: MagiskDB by inject()
|
||||
@Deprecated("Use dependency injection")
|
||||
val repoDB: RepoDatabaseHelper by inject()
|
||||
|
||||
@Volatile
|
||||
private var foreground: Activity? = null
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(base)
|
||||
if (BuildConfig.DEBUG)
|
||||
MultiDex.install(base)
|
||||
Timber.plant(Timber.DebugTree())
|
||||
|
||||
startKoin {
|
||||
androidContext(this@App)
|
||||
modules(koinModules)
|
||||
}
|
||||
|
||||
protectedContext = baseContext
|
||||
self = this
|
||||
deContext = base
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
protectedContext = base.createDeviceProtectedStorageContext()
|
||||
deContext = protectedContext
|
||||
deContext.moveSharedPreferencesFrom(base, base.defaultPrefsName)
|
||||
}
|
||||
|
||||
registerActivityLifecycleCallbacks(this)
|
||||
|
||||
Networking.init(base)
|
||||
LocaleManager.setLocale(this)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
LocaleManager.setLocale(this)
|
||||
}
|
||||
|
||||
//region ActivityLifecycleCallbacks
|
||||
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {}
|
||||
|
||||
@Synchronized
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
foreground = activity
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
foreground = null
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {}
|
||||
//endregion
|
||||
|
||||
private val Context.defaultPrefsName get() = "${packageName}_preferences"
|
||||
|
||||
companion object {
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Deprecated("Use dependency injection")
|
||||
@JvmStatic
|
||||
lateinit var self: App
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Deprecated("Use dependency injection; replace with protectedContext")
|
||||
@JvmStatic
|
||||
lateinit var deContext: Context
|
||||
|
||||
@Deprecated("Use Rx or similar")
|
||||
@JvmField
|
||||
var THREAD_POOL: ThreadPoolExecutor
|
||||
|
||||
init {
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER or Shell.FLAG_USE_MAGISK_BUSYBOX)
|
||||
Shell.Config.verboseLogging(BuildConfig.DEBUG)
|
||||
Shell.Config.addInitializers(RootUtils::class.java)
|
||||
Shell.Config.setTimeout(2)
|
||||
THREAD_POOL = AsyncTask.THREAD_POOL_EXECUTOR as ThreadPoolExecutor
|
||||
}
|
||||
|
||||
@Deprecated("")
|
||||
@JvmStatic
|
||||
fun foreground(): Activity? {
|
||||
val app: App by inject()
|
||||
return app.foreground
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@ package com.topjohnwu.magisk;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Xml;
|
||||
|
||||
import androidx.collection.ArrayMap;
|
||||
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
import com.topjohnwu.superuser.ShellUtils;
|
||||
@@ -17,6 +15,8 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import androidx.collection.ArrayMap;
|
||||
|
||||
public class Config {
|
||||
|
||||
// Current status
|
||||
@@ -109,14 +109,14 @@ public class Config {
|
||||
public static void export() {
|
||||
// Flush prefs to disk
|
||||
App app = App.self;
|
||||
app.prefs.edit().commit();
|
||||
app.getPrefs().edit().commit();
|
||||
File xml = new File(App.deContext.getFilesDir().getParent() + "/shared_prefs",
|
||||
app.getPackageName() + "_preferences.xml");
|
||||
Shell.su(Utils.fmt("cat %s > /data/adb/%s", xml, Const.MANAGER_CONFIGS)).exec();
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
SharedPreferences pref = App.self.prefs;
|
||||
SharedPreferences pref = App.self.getPrefs();
|
||||
SharedPreferences.Editor editor = pref.edit();
|
||||
File config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS);
|
||||
if (config.exists()) {
|
||||
@@ -238,19 +238,19 @@ public class Config {
|
||||
App app = App.self;
|
||||
switch (getConfigType(key)) {
|
||||
case PREF_INT:
|
||||
return (T) (Integer) app.prefs.getInt(key, getDef(key));
|
||||
return (T) (Integer) app.getPrefs().getInt(key, getDef(key));
|
||||
case PREF_STR_INT:
|
||||
return (T) (Integer) Utils.getPrefsInt(app.prefs, key, getDef(key));
|
||||
return (T) (Integer) Utils.getPrefsInt(app.getPrefs(), key, getDef(key));
|
||||
case PREF_BOOL:
|
||||
return (T) (Boolean) app.prefs.getBoolean(key, getDef(key));
|
||||
return (T) (Boolean) app.getPrefs().getBoolean(key, getDef(key));
|
||||
case PREF_STR:
|
||||
return (T) app.prefs.getString(key, getDef(key));
|
||||
return (T) app.getPrefs().getString(key, getDef(key));
|
||||
case DB_INT:
|
||||
return (T) (Integer) app.mDB.getSettings(key, getDef(key));
|
||||
return (T) (Integer) app.getDB().getSettings(key, getDef(key));
|
||||
case DB_BOOL:
|
||||
return (T) (Boolean) (app.mDB.getSettings(key, getDef(key) ? 1 : 0) != 0);
|
||||
return (T) (Boolean) (app.getDB().getSettings(key, getDef(key) ? 1 : 0) != 0);
|
||||
case DB_STR:
|
||||
return (T) app.mDB.getStrings(key, getDef(key));
|
||||
return (T) app.getDB().getStrings(key, getDef(key));
|
||||
}
|
||||
/* Will never get here (IllegalArgumentException in getConfigType) */
|
||||
return null;
|
||||
@@ -260,25 +260,25 @@ public class Config {
|
||||
App app = App.self;
|
||||
switch (getConfigType(key)) {
|
||||
case PREF_INT:
|
||||
app.prefs.edit().putInt(key, (int) val).apply();
|
||||
app.getPrefs().edit().putInt(key, (int) val).apply();
|
||||
break;
|
||||
case PREF_STR_INT:
|
||||
app.prefs.edit().putString(key, String.valueOf(val)).apply();
|
||||
app.getPrefs().edit().putString(key, String.valueOf(val)).apply();
|
||||
break;
|
||||
case PREF_BOOL:
|
||||
app.prefs.edit().putBoolean(key, (boolean) val).apply();
|
||||
app.getPrefs().edit().putBoolean(key, (boolean) val).apply();
|
||||
break;
|
||||
case PREF_STR:
|
||||
app.prefs.edit().putString(key, (String) val).apply();
|
||||
app.getPrefs().edit().putString(key, (String) val).apply();
|
||||
break;
|
||||
case DB_INT:
|
||||
app.mDB.setSettings(key, (int) val);
|
||||
app.getDB().setSettings(key, (int) val);
|
||||
break;
|
||||
case DB_BOOL:
|
||||
app.mDB.setSettings(key, (boolean) val ? 1 : 0);
|
||||
app.getDB().setSettings(key, (boolean) val ? 1 : 0);
|
||||
break;
|
||||
case DB_STR:
|
||||
app.mDB.setStrings(key, (String) val);
|
||||
app.getDB().setStrings(key, (String) val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -290,14 +290,14 @@ public class Config {
|
||||
case PREF_STR_INT:
|
||||
case PREF_BOOL:
|
||||
case PREF_STR:
|
||||
app.prefs.edit().remove(key).apply();
|
||||
app.getPrefs().edit().remove(key).apply();
|
||||
break;
|
||||
case DB_BOOL:
|
||||
case DB_INT:
|
||||
app.mDB.rmSettings(key);
|
||||
app.getDB().rmSettings(key);
|
||||
break;
|
||||
case DB_STR:
|
||||
app.mDB.setStrings(key, null);
|
||||
app.getDB().setStrings(key, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -365,13 +365,13 @@ public class Config {
|
||||
switch (type) {
|
||||
case DB_INT:
|
||||
editor.putString(key, String.valueOf(
|
||||
app.mDB.getSettings(key, (Integer) defs.get(key))));
|
||||
app.getDB().getSettings(key, (Integer) defs.get(key))));
|
||||
continue;
|
||||
case DB_STR:
|
||||
editor.putString(key, app.mDB.getStrings(key, (String) defs.get(key)));
|
||||
editor.putString(key, app.getDB().getStrings(key, (String) defs.get(key)));
|
||||
continue;
|
||||
case DB_BOOL:
|
||||
int bs = app.mDB.getSettings(key, -1);
|
||||
int bs = app.getDB().getSettings(key, -1);
|
||||
editor.putBoolean(key, bs < 0 ? (Boolean) defs.get(key) : bs != 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ public class Const {
|
||||
|
||||
public static final int USER_ID = Process.myUid() / 100000;
|
||||
|
||||
// Generic
|
||||
public static final String MAGISK_INSTALL_LOG_FILENAME = "magisk_install_log_%s.log";
|
||||
|
||||
public static final class MAGISK_VER {
|
||||
public static final int MIN_SUPPORT = 18000;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.skoumal.teanity.rxbus.RxBus
|
||||
import com.topjohnwu.magisk.App
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
val applicationModule = module {
|
||||
single { RxBus() }
|
||||
factory { get<Context>().resources }
|
||||
factory { get<Context>() as App }
|
||||
factory { get<Context>().packageManager }
|
||||
single(SUTimeout) {
|
||||
get<App>().protectedContext.getSharedPreferences("su_timeout", 0)
|
||||
}
|
||||
single { PreferenceManager.getDefaultSharedPreferences(get<App>().protectedContext) }
|
||||
}
|
||||
12
app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt
Normal file
12
app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
import com.topjohnwu.magisk.App
|
||||
import com.topjohnwu.magisk.data.database.MagiskDB
|
||||
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
val databaseModule = module {
|
||||
single { MagiskDB(get<App>().protectedContext) }
|
||||
single { RepoDatabaseHelper(get()) }
|
||||
}
|
||||
10
app/src/main/java/com/topjohnwu/magisk/di/MiscModule.kt
Normal file
10
app/src/main/java/com/topjohnwu/magisk/di/MiscModule.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
val miscModule = module {
|
||||
|
||||
// define miscs here
|
||||
|
||||
}
|
||||
10
app/src/main/java/com/topjohnwu/magisk/di/Modules.kt
Normal file
10
app/src/main/java/com/topjohnwu/magisk/di/Modules.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
val koinModules = listOf(
|
||||
applicationModule,
|
||||
networkingModule,
|
||||
databaseModule,
|
||||
repositoryModule,
|
||||
viewModelModules,
|
||||
miscModule
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
import org.koin.core.qualifier.named
|
||||
|
||||
val SUTimeout = named("su_timeout")
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
val networkingModule = module {}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
val repositoryModule = module {}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
import android.net.Uri
|
||||
import com.topjohnwu.magisk.ui.MainViewModel
|
||||
import com.topjohnwu.magisk.ui.flash.FlashViewModel
|
||||
import com.topjohnwu.magisk.ui.hide.HideViewModel
|
||||
import com.topjohnwu.magisk.ui.home.HomeViewModel
|
||||
import com.topjohnwu.magisk.ui.log.LogViewModel
|
||||
import com.topjohnwu.magisk.ui.module.ModuleViewModel
|
||||
import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel
|
||||
import com.topjohnwu.magisk.ui.surequest.SuRequestViewModel
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
||||
val viewModelModules = module {
|
||||
viewModel { MainViewModel() }
|
||||
viewModel { HomeViewModel(get()) }
|
||||
viewModel { SuperuserViewModel(get(), get(), get(), get()) }
|
||||
viewModel { HideViewModel(get(), get()) }
|
||||
viewModel { ModuleViewModel(get(), get()) }
|
||||
viewModel { LogViewModel(get(), get()) }
|
||||
viewModel { (action: String, uri: Uri?) -> FlashViewModel(action, uri, get()) }
|
||||
viewModel { SuRequestViewModel(get(), get(), get(SUTimeout), get()) }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,127 +0,0 @@
|
||||
package com.topjohnwu.magisk.model.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.model.entity.Module;
|
||||
import com.topjohnwu.magisk.view.SnackbarMaker;
|
||||
import com.topjohnwu.superuser.Shell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHolder> {
|
||||
|
||||
private final List<Module> mList;
|
||||
|
||||
public ModulesAdapter(List<Module> list) {
|
||||
mList = list;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||
Context context = holder.itemView.getContext();
|
||||
final Module module = mList.get(position);
|
||||
|
||||
String version = module.getVersion();
|
||||
String author = module.getAuthor();
|
||||
String description = module.getDescription();
|
||||
String noInfo = context.getString(R.string.no_info_provided);
|
||||
|
||||
holder.title.setText(module.getName());
|
||||
holder.versionName.setText(TextUtils.isEmpty(version) ? noInfo : version);
|
||||
holder.author.setText(TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
|
||||
holder.description.setText(TextUtils.isEmpty(description) ? noInfo : description);
|
||||
|
||||
holder.checkBox.setOnCheckedChangeListener(null);
|
||||
holder.checkBox.setChecked(module.isEnabled());
|
||||
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
|
||||
int snack;
|
||||
if (isChecked) {
|
||||
module.removeDisableFile();
|
||||
snack = R.string.disable_file_removed;
|
||||
} else {
|
||||
module.createDisableFile();
|
||||
snack = R.string.disable_file_created;
|
||||
}
|
||||
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
holder.delete.setOnClickListener(v -> {
|
||||
boolean removed = module.willBeRemoved();
|
||||
int snack;
|
||||
if (removed) {
|
||||
module.deleteRemoveFile();
|
||||
snack = R.string.remove_file_deleted;
|
||||
} else {
|
||||
module.createRemoveFile();
|
||||
snack = R.string.remove_file_created;
|
||||
}
|
||||
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
|
||||
updateDeleteButton(holder, module);
|
||||
});
|
||||
|
||||
if (module.isUpdated()) {
|
||||
holder.notice.setVisibility(View.VISIBLE);
|
||||
holder.notice.setText(R.string.update_file_created);
|
||||
holder.delete.setEnabled(false);
|
||||
} else {
|
||||
updateDeleteButton(holder, module);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDeleteButton(ViewHolder holder, Module module) {
|
||||
holder.notice.setVisibility(module.willBeRemoved() ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (module.willBeRemoved()) {
|
||||
holder.delete.setImageResource(R.drawable.ic_undelete);
|
||||
} else {
|
||||
holder.delete.setImageResource(R.drawable.ic_delete);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.title) TextView title;
|
||||
@BindView(R.id.version_name) TextView versionName;
|
||||
@BindView(R.id.description) TextView description;
|
||||
@BindView(R.id.notice) TextView notice;
|
||||
@BindView(R.id.checkbox) CheckBox checkBox;
|
||||
@BindView(R.id.author) TextView author;
|
||||
@BindView(R.id.delete) ImageView delete;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
new ModulesAdapter$ViewHolder_ViewBinding(this, itemView);
|
||||
|
||||
if (!Shell.rootAccess()) {
|
||||
checkBox.setEnabled(false);
|
||||
delete.setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
package com.topjohnwu.magisk.model.adapters;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.data.database.MagiskDB;
|
||||
import com.topjohnwu.magisk.model.entity.Policy;
|
||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||
import com.topjohnwu.magisk.view.ArrowExpandable;
|
||||
import com.topjohnwu.magisk.view.Expandable;
|
||||
import com.topjohnwu.magisk.view.ExpandableViewHolder;
|
||||
import com.topjohnwu.magisk.view.SnackbarMaker;
|
||||
import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog;
|
||||
import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
|
||||
|
||||
private List<Policy> policyList;
|
||||
private MagiskDB dbHelper;
|
||||
private PackageManager pm;
|
||||
private boolean[] expandList;
|
||||
|
||||
public PolicyAdapter(List<Policy> list, MagiskDB db, PackageManager pm) {
|
||||
policyList = list;
|
||||
expandList = new boolean[policyList.size()];
|
||||
dbHelper = db;
|
||||
this.pm = pm;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Policy policy = policyList.get(position);
|
||||
|
||||
holder.settings.setExpanded(expandList[position]);
|
||||
holder.trigger.setOnClickListener(view -> {
|
||||
if (holder.settings.isExpanded()) {
|
||||
holder.settings.collapse();
|
||||
expandList[position] = false;
|
||||
} else {
|
||||
holder.settings.expand();
|
||||
expandList[position] = true;
|
||||
}
|
||||
});
|
||||
|
||||
holder.appName.setText(policy.appName);
|
||||
holder.packageName.setText(policy.packageName);
|
||||
holder.appIcon.setImageDrawable(policy.info.loadIcon(pm));
|
||||
|
||||
holder.notificationSwitch.setOnCheckedChangeListener(null);
|
||||
holder.loggingSwitch.setOnCheckedChangeListener(null);
|
||||
|
||||
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
|
||||
holder.notificationSwitch.setChecked(policy.notification);
|
||||
holder.loggingSwitch.setChecked(policy.logging);
|
||||
|
||||
holder.masterSwitch.setOnClickListener(v -> {
|
||||
boolean isChecked = holder.masterSwitch.isChecked();
|
||||
Runnable r = () -> {
|
||||
if ((isChecked && policy.policy == Policy.DENY) ||
|
||||
(!isChecked && policy.policy == Policy.ALLOW)) {
|
||||
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
|
||||
String message = v.getContext().getString(
|
||||
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
|
||||
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
||||
dbHelper.updatePolicy(policy);
|
||||
}
|
||||
};
|
||||
if (FingerprintHelper.useFingerprint()) {
|
||||
holder.masterSwitch.setChecked(!isChecked);
|
||||
new FingerprintAuthDialog((Activity) v.getContext(), () -> {
|
||||
holder.masterSwitch.setChecked(isChecked);
|
||||
r.run();
|
||||
}).show();
|
||||
} else {
|
||||
r.run();
|
||||
}
|
||||
});
|
||||
holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> {
|
||||
if ((isChecked && !policy.notification) ||
|
||||
(!isChecked && policy.notification)) {
|
||||
policy.notification = isChecked;
|
||||
String message = v.getContext().getString(
|
||||
isChecked ? R.string.su_snack_notif_on : R.string.su_snack_notif_off, policy.appName);
|
||||
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
||||
dbHelper.updatePolicy(policy);
|
||||
}
|
||||
});
|
||||
holder.loggingSwitch.setOnCheckedChangeListener((v, isChecked) -> {
|
||||
if ((isChecked && !policy.logging) ||
|
||||
(!isChecked && policy.logging)) {
|
||||
policy.logging = isChecked;
|
||||
String message = v.getContext().getString(
|
||||
isChecked ? R.string.su_snack_log_on : R.string.su_snack_log_off, policy.appName);
|
||||
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
||||
dbHelper.updatePolicy(policy);
|
||||
}
|
||||
});
|
||||
holder.delete.setOnClickListener(v -> {
|
||||
DialogInterface.OnClickListener l = (dialog, which) -> {
|
||||
policyList.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
notifyItemRangeChanged(position, policyList.size());
|
||||
SnackbarMaker.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName),
|
||||
Snackbar.LENGTH_SHORT).show();
|
||||
dbHelper.deletePolicy(policy);
|
||||
};
|
||||
if (FingerprintHelper.useFingerprint()) {
|
||||
new FingerprintAuthDialog((Activity) v.getContext(),
|
||||
() -> l.onClick(null, 0)).show();
|
||||
} else {
|
||||
new CustomAlertDialog((Activity) v.getContext())
|
||||
.setTitle(R.string.su_revoke_title)
|
||||
.setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName))
|
||||
.setPositiveButton(R.string.yes, l)
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.setCancelable(true)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return policyList.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.app_name) TextView appName;
|
||||
@BindView(R.id.package_name) TextView packageName;
|
||||
@BindView(R.id.app_icon) ImageView appIcon;
|
||||
@BindView(R.id.master_switch) SwitchCompat masterSwitch;
|
||||
@BindView(R.id.notification_switch) SwitchCompat notificationSwitch;
|
||||
@BindView(R.id.logging_switch) SwitchCompat loggingSwitch;
|
||||
@BindView(R.id.expand_layout) ViewGroup expandLayout;
|
||||
@BindView(R.id.arrow) ImageView arrow;
|
||||
@BindView(R.id.trigger) View trigger;
|
||||
@BindView(R.id.delete) ImageView delete;
|
||||
@BindView(R.id.more_info) ImageView moreInfo;
|
||||
|
||||
Expandable settings;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
new PolicyAdapter$ViewHolder_ViewBinding(this, itemView);
|
||||
settings = new ArrowExpandable(new ExpandableViewHolder(expandLayout), arrow);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
package com.topjohnwu.magisk.model.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.topjohnwu.magisk.App;
|
||||
import com.topjohnwu.magisk.ClassMap;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper;
|
||||
import com.topjohnwu.magisk.model.download.DownloadModuleService;
|
||||
import com.topjohnwu.magisk.model.entity.Module;
|
||||
import com.topjohnwu.magisk.model.entity.Repo;
|
||||
import com.topjohnwu.magisk.ui.base.BaseActivity;
|
||||
import com.topjohnwu.magisk.utils.Event;
|
||||
import com.topjohnwu.magisk.view.MarkDownWindow;
|
||||
import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import butterknife.BindView;
|
||||
import java9.util.stream.StreamSupport;
|
||||
|
||||
public class ReposAdapter
|
||||
extends SectionedAdapter<ReposAdapter.SectionHolder, ReposAdapter.RepoHolder>
|
||||
implements Event.AutoListener, SearchView.OnQueryTextListener {
|
||||
|
||||
private static final int UPDATES = 0;
|
||||
private static final int INSTALLED = 1;
|
||||
private static final int OTHERS = 2;
|
||||
|
||||
private Map<String, Module> moduleMap;
|
||||
private RepoDatabaseHelper repoDB;
|
||||
private List<Pair<Integer, List<Repo>>> repoPairs;
|
||||
private List<Repo> fullList;
|
||||
private SearchView mSearch;
|
||||
|
||||
public ReposAdapter() {
|
||||
repoDB = App.self.repoDB;
|
||||
moduleMap = Collections.emptyMap();
|
||||
fullList = Collections.emptyList();
|
||||
repoPairs = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionCount() {
|
||||
return repoPairs.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(int section) {
|
||||
return repoPairs.get(section).second.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SectionHolder onCreateSectionViewHolder(ViewGroup parent) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.section, parent, false);
|
||||
return new SectionHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_repo, parent, false);
|
||||
return new RepoHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindSectionViewHolder(SectionHolder holder, int section) {
|
||||
switch (repoPairs.get(section).first) {
|
||||
case UPDATES:
|
||||
holder.sectionText.setText(R.string.update_available);
|
||||
break;
|
||||
case INSTALLED:
|
||||
holder.sectionText.setText(R.string.installed);
|
||||
break;
|
||||
case OTHERS:
|
||||
holder.sectionText.setText(R.string.not_installed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindItemViewHolder(RepoHolder holder, int section, int position) {
|
||||
Repo repo = repoPairs.get(section).second.get(position);
|
||||
Context context = holder.itemView.getContext();
|
||||
|
||||
String name = repo.getName();
|
||||
String version = repo.getVersion();
|
||||
String author = repo.getAuthor();
|
||||
String description = repo.getDescription();
|
||||
String noInfo = context.getString(R.string.no_info_provided);
|
||||
|
||||
holder.title.setText(TextUtils.isEmpty(name) ? noInfo : name);
|
||||
holder.versionName.setText(TextUtils.isEmpty(version) ? noInfo : version);
|
||||
holder.author.setText(TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
|
||||
holder.description.setText(TextUtils.isEmpty(description) ? noInfo : description);
|
||||
holder.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString()));
|
||||
|
||||
holder.infoLayout.setOnClickListener(v ->
|
||||
MarkDownWindow.show((BaseActivity) context, null, repo.getDetailUrl()));
|
||||
|
||||
holder.downloadImage.setOnClickListener(v -> {
|
||||
new CustomAlertDialog((BaseActivity) context)
|
||||
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
|
||||
.setMessage(context.getString(R.string.repo_install_msg, repo.getDownloadFilename()))
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.install, (d, i) ->
|
||||
startDownload((BaseActivity) context, repo, true))
|
||||
.setNeutralButton(R.string.download, (d, i) ->
|
||||
startDownload((BaseActivity) context, repo, false))
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
private void startDownload(BaseActivity activity, Repo repo, Boolean install) {
|
||||
activity.runWithExternalRW(() -> {
|
||||
Intent intent = new Intent(activity, ClassMap.get(DownloadModuleService.class))
|
||||
.putExtra("repo", repo).putExtra("install", install);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
activity.startForegroundService(intent);
|
||||
} else {
|
||||
activity.startService(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateLists() {
|
||||
if (mSearch != null)
|
||||
onQueryTextChange(mSearch.getQuery().toString());
|
||||
else
|
||||
onQueryTextChange("");
|
||||
}
|
||||
|
||||
private static boolean noCaseContain(String a, String b) {
|
||||
return a.toLowerCase().contains(b.toLowerCase());
|
||||
}
|
||||
|
||||
public void setSearchView(SearchView view) {
|
||||
mSearch = view;
|
||||
mSearch.setOnQueryTextListener(this);
|
||||
}
|
||||
|
||||
public void notifyDBChanged(boolean refresh) {
|
||||
try (Cursor c = repoDB.getRepoCursor()) {
|
||||
fullList = new ArrayList<>(c.getCount());
|
||||
while (c.moveToNext())
|
||||
fullList.add(new Repo(c));
|
||||
}
|
||||
if (refresh)
|
||||
updateLists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event) {
|
||||
moduleMap = Event.getResult(event);
|
||||
updateLists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getListeningEvents() {
|
||||
return new int[] {Event.MODULE_LOAD_DONE};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String s) {
|
||||
List<Repo> updates = new ArrayList<>();
|
||||
List<Repo> installed = new ArrayList<>();
|
||||
List<Repo> others = new ArrayList<>();
|
||||
|
||||
StreamSupport.stream(fullList)
|
||||
.filter(repo -> noCaseContain(repo.getName(), s)
|
||||
|| noCaseContain(repo.getAuthor(), s)
|
||||
|| noCaseContain(repo.getDescription(), s))
|
||||
.forEach(repo -> {
|
||||
Module module = moduleMap.get(repo.getId());
|
||||
if (module != null) {
|
||||
if (repo.getVersionCode() > module.getVersionCode()) {
|
||||
// Updates
|
||||
updates.add(repo);
|
||||
} else {
|
||||
installed.add(repo);
|
||||
}
|
||||
} else {
|
||||
others.add(repo);
|
||||
}
|
||||
});
|
||||
|
||||
repoPairs.clear();
|
||||
if (!updates.isEmpty())
|
||||
repoPairs.add(new Pair<>(UPDATES, updates));
|
||||
if (!installed.isEmpty())
|
||||
repoPairs.add(new Pair<>(INSTALLED, installed));
|
||||
if (!others.isEmpty())
|
||||
repoPairs.add(new Pair<>(OTHERS, others));
|
||||
|
||||
notifyDataSetChanged();
|
||||
return false;
|
||||
}
|
||||
|
||||
static class SectionHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.section_text) TextView sectionText;
|
||||
|
||||
SectionHolder(View itemView) {
|
||||
super(itemView);
|
||||
new ReposAdapter$SectionHolder_ViewBinding(this, itemView);
|
||||
}
|
||||
}
|
||||
|
||||
static class RepoHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.title) TextView title;
|
||||
@BindView(R.id.version_name) TextView versionName;
|
||||
@BindView(R.id.description) TextView description;
|
||||
@BindView(R.id.author) TextView author;
|
||||
@BindView(R.id.info_layout) View infoLayout;
|
||||
@BindView(R.id.download) ImageView downloadImage;
|
||||
@BindView(R.id.update_time) TextView updateTime;
|
||||
|
||||
RepoHolder(View itemView) {
|
||||
super(itemView);
|
||||
new ReposAdapter$RepoHolder_ViewBinding(this, itemView);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package com.topjohnwu.magisk.model.adapters;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public abstract class SectionedAdapter<S extends RecyclerView.ViewHolder, C extends RecyclerView.ViewHolder>
|
||||
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private static final int SECTION_TYPE = Integer.MIN_VALUE;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
final public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
if (viewType == SECTION_TYPE)
|
||||
return onCreateSectionViewHolder(parent);
|
||||
return onCreateItemViewHolder(parent, viewType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
final public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
PositionInfo info = getPositionInfo(position);
|
||||
if (info.position == -1)
|
||||
onBindSectionViewHolder((S) holder, info.section);
|
||||
else
|
||||
onBindItemViewHolder((C) holder, info.section, info.position);
|
||||
}
|
||||
|
||||
@Override
|
||||
final public int getItemCount() {
|
||||
int size, sec;
|
||||
size = sec = getSectionCount();
|
||||
for (int i = 0; i < sec; ++i){
|
||||
size += getItemCount(i);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
final public int getItemViewType(int position) {
|
||||
PositionInfo info = getPositionInfo(position);
|
||||
if (info.position == -1)
|
||||
return SECTION_TYPE;
|
||||
else
|
||||
return getItemViewType(info.section, info.position);
|
||||
}
|
||||
|
||||
public int getItemViewType(int section, int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected int getSectionPosition(int section) {
|
||||
return getItemPosition(section, -1);
|
||||
}
|
||||
|
||||
protected int getItemPosition(int section, int position) {
|
||||
int realPosition = 0;
|
||||
// Previous sections
|
||||
for (int i = 0; i < section; ++i) {
|
||||
realPosition += getItemCount(i) + 1;
|
||||
}
|
||||
// Current section
|
||||
realPosition += position + 1;
|
||||
return realPosition;
|
||||
}
|
||||
|
||||
private PositionInfo getPositionInfo(int position) {
|
||||
int section = 0;
|
||||
while (true) {
|
||||
if (position == 0)
|
||||
return new PositionInfo(section, -1);
|
||||
position -= 1;
|
||||
if (position < getItemCount(section))
|
||||
return new PositionInfo(section, position);
|
||||
position -= getItemCount(section++);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PositionInfo {
|
||||
int section;
|
||||
int position;
|
||||
PositionInfo(int section, int position) {
|
||||
this.section = section;
|
||||
this.position = position;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract int getSectionCount();
|
||||
public abstract int getItemCount(int section);
|
||||
public abstract S onCreateSectionViewHolder(ViewGroup parent);
|
||||
public abstract C onCreateItemViewHolder(ViewGroup parent, int viewType);
|
||||
public abstract void onBindSectionViewHolder(S holder, int section);
|
||||
public abstract void onBindItemViewHolder(C holder, int section, int position);
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package com.topjohnwu.magisk.model.adapters;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class StringListAdapter<VH extends StringListAdapter.ViewHolder>
|
||||
extends RecyclerView.Adapter<VH> {
|
||||
|
||||
private RecyclerView rv;
|
||||
private boolean dynamic;
|
||||
private int screenWidth;
|
||||
private int txtWidth = -1;
|
||||
private int padding;
|
||||
|
||||
protected List<String> mList;
|
||||
|
||||
public StringListAdapter(List<String> list) {
|
||||
this(list, false);
|
||||
}
|
||||
|
||||
public StringListAdapter(List<String> list, boolean isDynamic) {
|
||||
mList = list;
|
||||
dynamic = isDynamic;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(itemLayoutRes(), parent, false);
|
||||
VH vh = createViewHolder(v);
|
||||
if (txtWidth < 0)
|
||||
onUpdateTextWidth(vh);
|
||||
return vh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
holder.txt.setText(mList.get(position));
|
||||
holder.txt.getLayoutParams().width = txtWidth;
|
||||
if (dynamic)
|
||||
onUpdateTextWidth(holder);
|
||||
}
|
||||
|
||||
protected void onUpdateTextWidth(VH vh) {
|
||||
if (txtWidth < 0) {
|
||||
txtWidth = screenWidth - padding;
|
||||
} else {
|
||||
vh.txt.measure(0, 0);
|
||||
int width = vh.txt.getMeasuredWidth();
|
||||
if (width > txtWidth) {
|
||||
txtWidth = width;
|
||||
vh.txt.getLayoutParams().width = txtWidth;
|
||||
}
|
||||
}
|
||||
if (rv.getWidth() != txtWidth + padding)
|
||||
rv.requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToRecyclerView(@NonNull RecyclerView rv) {
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
((Activity) rv.getContext()).getWindowManager()
|
||||
.getDefaultDisplay().getMetrics(displayMetrics);
|
||||
screenWidth = displayMetrics.widthPixels;
|
||||
padding = rv.getPaddingStart() + rv.getPaddingEnd();
|
||||
this.rv = rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getItemCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
protected abstract int itemLayoutRes();
|
||||
|
||||
@NonNull
|
||||
public abstract VH createViewHolder(@NonNull View v);
|
||||
|
||||
public static abstract class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView txt;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
txt = itemView.findViewById(textViewResId());
|
||||
}
|
||||
|
||||
@IdRes
|
||||
protected abstract int textViewResId();
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
package com.topjohnwu.magisk.model.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.RotateAnimation;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.data.database.MagiskDB;
|
||||
import com.topjohnwu.magisk.model.entity.SuLogEntry;
|
||||
import com.topjohnwu.magisk.view.ExpandableViewHolder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, SuLogAdapter.LogViewHolder> {
|
||||
|
||||
private List<List<SuLogEntry>> logEntries;
|
||||
private Set<Integer> itemExpanded, sectionExpanded;
|
||||
private MagiskDB suDB;
|
||||
|
||||
public SuLogAdapter(MagiskDB db) {
|
||||
suDB = db;
|
||||
logEntries = Collections.emptyList();
|
||||
sectionExpanded = new HashSet<>();
|
||||
itemExpanded = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionCount() {
|
||||
return logEntries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(int section) {
|
||||
return sectionExpanded.contains(section) ? logEntries.get(section).size() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SectionHolder onCreateSectionViewHolder(ViewGroup parent) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog_group, parent, false);
|
||||
return new SectionHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog, parent, false);
|
||||
return new LogViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindSectionViewHolder(SectionHolder holder, int section) {
|
||||
SuLogEntry entry = logEntries.get(section).get(0);
|
||||
holder.arrow.setRotation(sectionExpanded.contains(section) ? 180 : 0);
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
RotateAnimation rotate;
|
||||
if (sectionExpanded.contains(section)) {
|
||||
holder.arrow.setRotation(0);
|
||||
rotate = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||
sectionExpanded.remove(section);
|
||||
notifyItemRangeRemoved(getItemPosition(section, 0), logEntries.get(section).size());
|
||||
} else {
|
||||
rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||
sectionExpanded.add(section);
|
||||
notifyItemRangeInserted(getItemPosition(section, 0), logEntries.get(section).size());
|
||||
}
|
||||
rotate.setDuration(300);
|
||||
rotate.setFillAfter(true);
|
||||
holder.arrow.setAnimation(rotate);
|
||||
});
|
||||
holder.date.setText(entry.getDateString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindItemViewHolder(LogViewHolder holder, int section, int position) {
|
||||
SuLogEntry entry = logEntries.get(section).get(position);
|
||||
int realIdx = getItemPosition(section, position);
|
||||
holder.expandable.setExpanded(itemExpanded.contains(realIdx));
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
if (holder.expandable.isExpanded()) {
|
||||
holder.expandable.collapse();
|
||||
itemExpanded.remove(realIdx);
|
||||
} else {
|
||||
holder.expandable.expand();
|
||||
itemExpanded.add(realIdx);
|
||||
}
|
||||
});
|
||||
Context context = holder.itemView.getContext();
|
||||
holder.appName.setText(entry.appName);
|
||||
holder.action.setText(entry.action ? R.string.grant : R.string.deny);
|
||||
holder.pid.setText(context.getString(R.string.pid, entry.fromPid));
|
||||
holder.uid.setText(context.getString(R.string.target_uid, entry.toUid));
|
||||
holder.command.setText(context.getString(R.string.command, entry.command));
|
||||
holder.time.setText(entry.getTimeString());
|
||||
}
|
||||
|
||||
public void notifyDBChanged() {
|
||||
logEntries = suDB.getLogs();
|
||||
itemExpanded.clear();
|
||||
sectionExpanded.clear();
|
||||
sectionExpanded.add(0);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
static class SectionHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.date) TextView date;
|
||||
@BindView(R.id.arrow) ImageView arrow;
|
||||
|
||||
SectionHolder(View itemView) {
|
||||
super(itemView);
|
||||
new SuLogAdapter$SectionHolder_ViewBinding(this, itemView);
|
||||
}
|
||||
}
|
||||
|
||||
static class LogViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@BindView(R.id.app_name) TextView appName;
|
||||
@BindView(R.id.action) TextView action;
|
||||
@BindView(R.id.time) TextView time;
|
||||
@BindView(R.id.pid) TextView pid;
|
||||
@BindView(R.id.uid) TextView uid;
|
||||
@BindView(R.id.cmd) TextView command;
|
||||
@BindView(R.id.expand_layout) ViewGroup expandLayout;
|
||||
|
||||
ExpandableViewHolder expandable;
|
||||
|
||||
LogViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
new SuLogAdapter$LogViewHolder_ViewBinding(this, itemView);
|
||||
expandable = new ExpandableViewHolder(expandLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.topjohnwu.magisk.model.adapters;
|
||||
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TabFragmentAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private List<Fragment> fragmentList;
|
||||
private List<String> titleList;
|
||||
|
||||
public TabFragmentAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
fragmentList = new ArrayList<>();
|
||||
titleList = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return fragmentList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return fragmentList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return titleList.get(position);
|
||||
}
|
||||
|
||||
public void addTab(Fragment fragment, String title) {
|
||||
fragmentList.add(fragment);
|
||||
titleList.add(title);
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.IBinder;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.topjohnwu.magisk.App;
|
||||
import com.topjohnwu.magisk.ClassMap;
|
||||
import com.topjohnwu.magisk.Const;
|
||||
@@ -31,6 +29,8 @@ import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class DownloadModuleService extends Service {
|
||||
|
||||
private List<ProgressNotification> notifications;
|
||||
|
||||
@@ -5,10 +5,10 @@ import android.database.Cursor;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public abstract class BaseModule implements Comparable<BaseModule>, Parcelable {
|
||||
|
||||
private String mId, mName, mVersion, mAuthor, mDescription;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.topjohnwu.magisk.model.entity
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.topjohnwu.magisk.utils.packageInfo
|
||||
import com.topjohnwu.magisk.utils.processes
|
||||
|
||||
class HideAppInfo(
|
||||
val info: ApplicationInfo,
|
||||
val name: String,
|
||||
val icon: Drawable
|
||||
) {
|
||||
|
||||
val processes = info.packageInfo?.processes?.distinct() ?: listOf(info.packageName)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.topjohnwu.magisk.model.entity
|
||||
|
||||
class HideTarget(line: String) {
|
||||
|
||||
private val split = line.split(Regex("\\|"), 2)
|
||||
|
||||
val packageName = split[0]
|
||||
val process = split.getOrElse(1) { packageName }
|
||||
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import android.content.ContentValues;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
|
||||
public class Policy implements Comparable<Policy>{
|
||||
public static final int INTERACTIVE = 0;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.topjohnwu.magisk.model.entity.recycler
|
||||
|
||||
import com.skoumal.teanity.databinding.ComparableRvItem
|
||||
import com.topjohnwu.magisk.R
|
||||
|
||||
class ConsoleRvItem(val item: String) : ComparableRvItem<ConsoleRvItem>() {
|
||||
override val layoutRes: Int = R.layout.item_console
|
||||
|
||||
override fun contentSameAs(other: ConsoleRvItem) = itemSameAs(other)
|
||||
override fun itemSameAs(other: ConsoleRvItem) = item == other.item
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.topjohnwu.magisk.model.entity.recycler
|
||||
|
||||
import com.skoumal.teanity.databinding.ComparableRvItem
|
||||
import com.skoumal.teanity.extensions.addOnPropertyChangedCallback
|
||||
import com.skoumal.teanity.rxbus.RxBus
|
||||
import com.skoumal.teanity.util.DiffObservableList
|
||||
import com.skoumal.teanity.util.KObservableField
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.model.entity.HideAppInfo
|
||||
import com.topjohnwu.magisk.model.entity.HideTarget
|
||||
import com.topjohnwu.magisk.model.entity.state.IndeterminateState
|
||||
import com.topjohnwu.magisk.model.events.HideProcessEvent
|
||||
import com.topjohnwu.magisk.utils.inject
|
||||
import com.topjohnwu.magisk.utils.toggle
|
||||
|
||||
class HideRvItem(val item: HideAppInfo, targets: List<HideTarget>) :
|
||||
ComparableRvItem<HideRvItem>() {
|
||||
|
||||
override val layoutRes: Int = R.layout.item_hide_app
|
||||
|
||||
val packageName = item.info.packageName.orEmpty()
|
||||
val items = DiffObservableList(callback).also {
|
||||
val items = item.processes.map {
|
||||
val isHidden = targets.any { target ->
|
||||
packageName == target.packageName && it == target.process
|
||||
}
|
||||
HideProcessRvItem(packageName, it, isHidden)
|
||||
}
|
||||
it.update(items)
|
||||
}
|
||||
val isHiddenState = KObservableField(currentState)
|
||||
val isExpanded = KObservableField(false)
|
||||
|
||||
private val itemsProcess get() = items.filterIsInstance<HideProcessRvItem>()
|
||||
|
||||
private val currentState
|
||||
get() = when (itemsProcess.count { it.isHidden.value }) {
|
||||
items.size -> IndeterminateState.CHECKED
|
||||
in 1 until items.size -> IndeterminateState.INDETERMINATE
|
||||
else -> IndeterminateState.UNCHECKED
|
||||
}
|
||||
|
||||
init {
|
||||
itemsProcess.forEach {
|
||||
it.isHidden.addOnPropertyChangedCallback { isHiddenState.value = currentState }
|
||||
}
|
||||
}
|
||||
|
||||
fun toggle() {
|
||||
val desiredState = when (isHiddenState.value) {
|
||||
IndeterminateState.INDETERMINATE,
|
||||
IndeterminateState.UNCHECKED -> true
|
||||
IndeterminateState.CHECKED -> false
|
||||
}
|
||||
itemsProcess.forEach { it.isHidden.value = desiredState }
|
||||
isHiddenState.value = currentState
|
||||
}
|
||||
|
||||
fun toggleExpansion() {
|
||||
if (items.size <= 1) return
|
||||
isExpanded.toggle()
|
||||
}
|
||||
|
||||
override fun contentSameAs(other: HideRvItem): Boolean = items.all { other.items.contains(it) }
|
||||
override fun itemSameAs(other: HideRvItem): Boolean = item.info == other.item.info
|
||||
|
||||
}
|
||||
|
||||
class HideProcessRvItem(
|
||||
val packageName: String,
|
||||
val process: String,
|
||||
isHidden: Boolean
|
||||
) : ComparableRvItem<HideProcessRvItem>() {
|
||||
|
||||
override val layoutRes: Int = R.layout.item_hide_process
|
||||
|
||||
val isHidden = KObservableField(isHidden)
|
||||
|
||||
private val rxBus: RxBus by inject()
|
||||
|
||||
init {
|
||||
this.isHidden.addOnPropertyChangedCallback {
|
||||
rxBus.post(HideProcessEvent(this@HideProcessRvItem))
|
||||
}
|
||||
}
|
||||
|
||||
fun toggle() = isHidden.toggle()
|
||||
|
||||
override fun contentSameAs(other: HideProcessRvItem): Boolean = itemSameAs(other)
|
||||
override fun itemSameAs(other: HideProcessRvItem): Boolean =
|
||||
packageName == other.packageName && process == other.process
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.topjohnwu.magisk.model.entity.recycler
|
||||
|
||||
import androidx.databinding.ObservableList
|
||||
import com.skoumal.teanity.databinding.ComparableRvItem
|
||||
import com.skoumal.teanity.util.DiffObservableList
|
||||
import com.skoumal.teanity.util.KObservableField
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.model.entity.SuLogEntry
|
||||
import com.topjohnwu.magisk.utils.toggle
|
||||
|
||||
class LogRvItem : ComparableRvItem<LogRvItem>() {
|
||||
override val layoutRes: Int = R.layout.item_page_log
|
||||
|
||||
val items = DiffObservableList(callback)
|
||||
|
||||
fun update(list: List<LogItemRvItem>) {
|
||||
list.firstOrNull()?.isExpanded?.value = true
|
||||
items.update(list)
|
||||
}
|
||||
|
||||
//two of these will never be present, safe to assume it's unique
|
||||
override fun contentSameAs(other: LogRvItem): Boolean = false
|
||||
|
||||
override fun itemSameAs(other: LogRvItem): Boolean = false
|
||||
}
|
||||
|
||||
class LogItemRvItem(
|
||||
val items: ObservableList<ComparableRvItem<*>>
|
||||
) : ComparableRvItem<LogItemRvItem>() {
|
||||
override val layoutRes: Int = R.layout.item_superuser_log
|
||||
|
||||
val date = items.filterIsInstance<LogItemEntryRvItem>().firstOrNull()
|
||||
?.item?.dateString.orEmpty()
|
||||
val isExpanded = KObservableField(false)
|
||||
|
||||
fun toggle() = isExpanded.toggle()
|
||||
|
||||
override fun contentSameAs(other: LogItemRvItem): Boolean = items
|
||||
.any { !other.items.contains(it) }
|
||||
|
||||
override fun itemSameAs(other: LogItemRvItem): Boolean = date == other.date
|
||||
}
|
||||
|
||||
class LogItemEntryRvItem(val item: SuLogEntry) : ComparableRvItem<LogItemEntryRvItem>() {
|
||||
override val layoutRes: Int = R.layout.item_superuser_log_entry
|
||||
|
||||
val isExpanded = KObservableField(false)
|
||||
|
||||
fun toggle() = isExpanded.toggle()
|
||||
|
||||
override fun contentSameAs(other: LogItemEntryRvItem) = item.fromUid == other.item.fromUid &&
|
||||
item.toUid == other.item.toUid &&
|
||||
item.fromPid == other.item.fromPid &&
|
||||
item.packageName == other.item.packageName &&
|
||||
item.command == other.item.command &&
|
||||
item.action == other.item.action &&
|
||||
item.date == other.item.date
|
||||
|
||||
override fun itemSameAs(other: LogItemEntryRvItem) = item.appName == other.item.appName
|
||||
}
|
||||
|
||||
class MagiskLogRvItem : ComparableRvItem<MagiskLogRvItem>() {
|
||||
override val layoutRes: Int = R.layout.item_page_magisk_log
|
||||
|
||||
val items = DiffObservableList(callback)
|
||||
|
||||
fun update(list: List<ConsoleRvItem>) {
|
||||
items.update(list)
|
||||
}
|
||||
|
||||
//two of these will never be present, safe to assume it's unique
|
||||
override fun contentSameAs(other: MagiskLogRvItem): Boolean = false
|
||||
|
||||
override fun itemSameAs(other: MagiskLogRvItem): Boolean = false
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.topjohnwu.magisk.model.entity.recycler
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.StringRes
|
||||
import com.skoumal.teanity.databinding.ComparableRvItem
|
||||
import com.skoumal.teanity.extensions.addOnPropertyChangedCallback
|
||||
import com.skoumal.teanity.util.KObservableField
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.model.entity.Module
|
||||
import com.topjohnwu.magisk.model.entity.Repo
|
||||
import com.topjohnwu.magisk.utils.get
|
||||
import com.topjohnwu.magisk.utils.toggle
|
||||
|
||||
class ModuleRvItem(val item: Module) : ComparableRvItem<ModuleRvItem>() {
|
||||
|
||||
override val layoutRes: Int = R.layout.item_module
|
||||
|
||||
val lastActionNotice = KObservableField("")
|
||||
val isChecked = KObservableField(item.isEnabled)
|
||||
val isDeletable = KObservableField(item.willBeRemoved())
|
||||
|
||||
init {
|
||||
isChecked.addOnPropertyChangedCallback {
|
||||
when (it) {
|
||||
true -> item.removeDisableFile().notice(R.string.disable_file_removed)
|
||||
false -> item.createDisableFile().notice(R.string.disable_file_created)
|
||||
}
|
||||
}
|
||||
isDeletable.addOnPropertyChangedCallback {
|
||||
when (it) {
|
||||
true -> item.createRemoveFile().notice(R.string.remove_file_created)
|
||||
false -> item.deleteRemoveFile().notice(R.string.remove_file_deleted)
|
||||
}
|
||||
}
|
||||
when {
|
||||
item.isUpdated -> notice(R.string.update_file_created)
|
||||
item.willBeRemoved() -> notice(R.string.remove_file_created)
|
||||
}
|
||||
}
|
||||
|
||||
fun toggle() = isChecked.toggle()
|
||||
fun toggleDelete() = isDeletable.toggle()
|
||||
|
||||
@Suppress("unused")
|
||||
private fun Any.notice(@StringRes info: Int) {
|
||||
lastActionNotice.value = get<Resources>().getString(info)
|
||||
}
|
||||
|
||||
override fun contentSameAs(other: ModuleRvItem): Boolean = item.version == other.item.version
|
||||
&& item.versionCode == other.item.versionCode
|
||||
&& item.description == other.item.description
|
||||
|
||||
override fun itemSameAs(other: ModuleRvItem): Boolean = item.name == other.item.name
|
||||
}
|
||||
|
||||
class RepoRvItem(val item: Repo) : ComparableRvItem<RepoRvItem>() {
|
||||
|
||||
override val layoutRes: Int = R.layout.item_repo
|
||||
|
||||
override fun contentSameAs(other: RepoRvItem): Boolean = item.version == other.item.version
|
||||
&& item.lastUpdate == other.item.lastUpdate
|
||||
&& item.versionCode == other.item.versionCode
|
||||
&& item.description == other.item.description
|
||||
|
||||
override fun itemSameAs(other: RepoRvItem): Boolean = item.detailUrl == other.item.detailUrl
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user