mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-04-16 16:51:25 +00:00
Rewrite configs with Kotlin delagate properties
This commit is contained in:
parent
3e58d502d0
commit
7756e10779
@ -1,399 +0,0 @@
|
|||||||
package com.topjohnwu.magisk;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.util.Xml;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
|
||||||
import com.topjohnwu.superuser.io.SuFileInputStream;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.collection.ArrayMap;
|
|
||||||
|
|
||||||
import static com.topjohnwu.magisk.ConfigLeanback.getPrefs;
|
|
||||||
import static com.topjohnwu.magisk.utils.XAndroidKt.getPackageName;
|
|
||||||
|
|
||||||
public final class Config {
|
|
||||||
|
|
||||||
private static final ArrayMap<String, Object> defs = new ArrayMap<>();
|
|
||||||
public static int magiskVersionCode = -1;
|
|
||||||
// Current status
|
|
||||||
public static String magiskVersionString = "";
|
|
||||||
// Update Info
|
|
||||||
public static String remoteMagiskVersionString = "";
|
|
||||||
public static int remoteMagiskVersionCode = -1;
|
|
||||||
public static String magiskLink = "";
|
|
||||||
public static String magiskNoteLink = "";
|
|
||||||
public static String magiskMD5 = "";
|
|
||||||
public static String remoteManagerVersionString = "";
|
|
||||||
public static int remoteManagerVersionCode = -1;
|
|
||||||
public static String managerLink = "";
|
|
||||||
public static String managerNoteLink = "";
|
|
||||||
public static String uninstallerLink = "";
|
|
||||||
|
|
||||||
// Install flags
|
|
||||||
public static boolean keepVerity = false;
|
|
||||||
public static boolean keepEnc = false;
|
|
||||||
public static boolean recovery = false;
|
|
||||||
|
|
||||||
public static int suLogTimeout = 14;
|
|
||||||
|
|
||||||
public static class Key {
|
|
||||||
// su configs
|
|
||||||
public static final String ROOT_ACCESS = "root_access";
|
|
||||||
public static final String SU_MULTIUSER_MODE = "multiuser_mode";
|
|
||||||
public static final String SU_MNT_NS = "mnt_ns";
|
|
||||||
public static final String SU_MANAGER = "requester";
|
|
||||||
public static final String SU_REQUEST_TIMEOUT = "su_request_timeout";
|
|
||||||
public static final String SU_AUTO_RESPONSE = "su_auto_response";
|
|
||||||
public static final String SU_NOTIFICATION = "su_notification";
|
|
||||||
public static final String SU_REAUTH = "su_reauth";
|
|
||||||
public static final String SU_FINGERPRINT = "su_fingerprint";
|
|
||||||
|
|
||||||
// prefs
|
|
||||||
public static final String CHECK_UPDATES = "check_update";
|
|
||||||
public static final String UPDATE_CHANNEL = "update_channel";
|
|
||||||
public static final String CUSTOM_CHANNEL = "custom_channel";
|
|
||||||
public static final String LOCALE = "locale";
|
|
||||||
public static final String DARK_THEME = "dark_theme";
|
|
||||||
public static final String ETAG_KEY = "ETag";
|
|
||||||
public static final String REPO_ORDER = "repo_order";
|
|
||||||
public static final String SHOW_SYSTEM_APP = "show_system";
|
|
||||||
|
|
||||||
// system state
|
|
||||||
public static final String UPDATE_SERVICE_VER = "update_service_version";
|
|
||||||
public static final String MAGISKHIDE = "magiskhide";
|
|
||||||
public static final String COREONLY = "disable";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Value {
|
|
||||||
public static final int DEFAULT_CHANNEL = -1;
|
|
||||||
public static final int STABLE_CHANNEL = 0;
|
|
||||||
public static final int BETA_CHANNEL = 1;
|
|
||||||
public static final int CUSTOM_CHANNEL = 2;
|
|
||||||
public static final int CANARY_CHANNEL = 3;
|
|
||||||
public static final int CANARY_DEBUG_CHANNEL = 4;
|
|
||||||
public static final int ROOT_ACCESS_DISABLED = 0;
|
|
||||||
public static final int ROOT_ACCESS_APPS_ONLY = 1;
|
|
||||||
public static final int ROOT_ACCESS_ADB_ONLY = 2;
|
|
||||||
public static final int ROOT_ACCESS_APPS_AND_ADB = 3;
|
|
||||||
public static final int MULTIUSER_MODE_OWNER_ONLY = 0;
|
|
||||||
public static final int MULTIUSER_MODE_OWNER_MANAGED = 1;
|
|
||||||
public static final int MULTIUSER_MODE_USER = 2;
|
|
||||||
public static final int NAMESPACE_MODE_GLOBAL = 0;
|
|
||||||
public static final int NAMESPACE_MODE_REQUESTER = 1;
|
|
||||||
public static final int NAMESPACE_MODE_ISOLATE = 2;
|
|
||||||
public static final int NO_NOTIFICATION = 0;
|
|
||||||
public static final int NOTIFICATION_TOAST = 1;
|
|
||||||
public static final int SU_PROMPT = 0;
|
|
||||||
public static final int SU_AUTO_DENY = 1;
|
|
||||||
public static final int SU_AUTO_ALLOW = 2;
|
|
||||||
public static final int[] TIMEOUT_LIST = {0, -1, 10, 20, 30, 60};
|
|
||||||
public static final int ORDER_NAME = 0;
|
|
||||||
public static final int ORDER_DATE = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean magiskHide = false;
|
|
||||||
|
|
||||||
public static void loadMagiskInfo() {
|
|
||||||
try {
|
|
||||||
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
|
|
||||||
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
|
|
||||||
magiskHide = Shell.su("magiskhide --status").exec().isSuccess();
|
|
||||||
} catch (NumberFormatException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void export() {
|
|
||||||
// Flush prefs to disk
|
|
||||||
getPrefs().edit().apply();
|
|
||||||
Context context = ConfigLeanback.getProtectedContext();
|
|
||||||
File xml = new File(context.getFilesDir().getParent() + "/shared_prefs",
|
|
||||||
getPackageName() + "_preferences.xml");
|
|
||||||
Shell.su(Utils.fmt("cat %s > /data/adb/%s", xml, Const.MANAGER_CONFIGS)).exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int PREF_INT = 0;
|
|
||||||
private static final int PREF_STR_INT = 1;
|
|
||||||
private static final int PREF_BOOL = 2;
|
|
||||||
private static final int PREF_STR = 3;
|
|
||||||
private static final int DB_INT = 4;
|
|
||||||
private static final int DB_BOOL = 5;
|
|
||||||
private static final int DB_STR = 6;
|
|
||||||
|
|
||||||
private static int getConfigType(String key) {
|
|
||||||
switch (key) {
|
|
||||||
case Key.REPO_ORDER:
|
|
||||||
return PREF_INT;
|
|
||||||
|
|
||||||
case Key.SU_REQUEST_TIMEOUT:
|
|
||||||
case Key.SU_AUTO_RESPONSE:
|
|
||||||
case Key.SU_NOTIFICATION:
|
|
||||||
case Key.UPDATE_CHANNEL:
|
|
||||||
return PREF_STR_INT;
|
|
||||||
|
|
||||||
case Key.DARK_THEME:
|
|
||||||
case Key.SU_REAUTH:
|
|
||||||
case Key.CHECK_UPDATES:
|
|
||||||
case Key.MAGISKHIDE:
|
|
||||||
case Key.COREONLY:
|
|
||||||
case Key.SHOW_SYSTEM_APP:
|
|
||||||
return PREF_BOOL;
|
|
||||||
|
|
||||||
case Key.CUSTOM_CHANNEL:
|
|
||||||
case Key.LOCALE:
|
|
||||||
case Key.ETAG_KEY:
|
|
||||||
return PREF_STR;
|
|
||||||
|
|
||||||
case Key.ROOT_ACCESS:
|
|
||||||
case Key.SU_MNT_NS:
|
|
||||||
case Key.SU_MULTIUSER_MODE:
|
|
||||||
return DB_INT;
|
|
||||||
|
|
||||||
case Key.SU_FINGERPRINT:
|
|
||||||
return DB_BOOL;
|
|
||||||
|
|
||||||
case Key.SU_MANAGER:
|
|
||||||
return DB_STR;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void initialize() {
|
|
||||||
SharedPreferences pref = getPrefs();
|
|
||||||
SharedPreferences.Editor editor = pref.edit();
|
|
||||||
File config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS);
|
|
||||||
if (config.exists()) {
|
|
||||||
try {
|
|
||||||
SuFileInputStream is = new SuFileInputStream(config);
|
|
||||||
XmlPullParser parser = Xml.newPullParser();
|
|
||||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
|
||||||
parser.setInput(is, "UTF-8");
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "map");
|
|
||||||
while (parser.next() != XmlPullParser.END_TAG) {
|
|
||||||
if (parser.getEventType() != XmlPullParser.START_TAG)
|
|
||||||
continue;
|
|
||||||
String key = parser.getAttributeValue(null, "name");
|
|
||||||
String value = parser.getAttributeValue(null, "value");
|
|
||||||
switch (parser.getName()) {
|
|
||||||
case "string":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "string");
|
|
||||||
editor.putString(key, parser.nextText());
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "string");
|
|
||||||
break;
|
|
||||||
case "boolean":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "boolean");
|
|
||||||
editor.putBoolean(key, Boolean.parseBoolean(value));
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "boolean");
|
|
||||||
break;
|
|
||||||
case "int":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "int");
|
|
||||||
editor.putInt(key, Integer.parseInt(value));
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "int");
|
|
||||||
break;
|
|
||||||
case "long":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "long");
|
|
||||||
editor.putLong(key, Long.parseLong(value));
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "long");
|
|
||||||
break;
|
|
||||||
case "float":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "int");
|
|
||||||
editor.putFloat(key, Float.parseFloat(value));
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "int");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException | XmlPullParserException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
editor.remove(Key.ETAG_KEY);
|
|
||||||
editor.apply();
|
|
||||||
editor = pref.edit();
|
|
||||||
config.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set defaults if not set
|
|
||||||
setDefs(pref, editor);
|
|
||||||
|
|
||||||
// These settings are from actual device state
|
|
||||||
editor.putBoolean(Key.MAGISKHIDE, magiskHide)
|
|
||||||
.putBoolean(Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
|
|
||||||
.putInt(Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
|
|
||||||
.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T> T get(String key) {
|
|
||||||
switch (getConfigType(key)) {
|
|
||||||
case PREF_INT:
|
|
||||||
return (T) (Integer) getPrefs().getInt(key, getDef(key));
|
|
||||||
case PREF_STR_INT:
|
|
||||||
return (T) (Integer) Utils.getPrefsInt(getPrefs(), key, getDef(key));
|
|
||||||
case PREF_BOOL:
|
|
||||||
return (T) (Boolean) getPrefs().getBoolean(key, getDef(key));
|
|
||||||
case PREF_STR:
|
|
||||||
return (T) getPrefs().getString(key, getDef(key));
|
|
||||||
case DB_INT:
|
|
||||||
return (T) (Integer) ConfigLeanback.get(key, (Integer) getDef(key));
|
|
||||||
case DB_BOOL:
|
|
||||||
return (T) (Boolean) (ConfigLeanback.get(key, getDef(key) ? 1 : 0) != 0);
|
|
||||||
case DB_STR:
|
|
||||||
return (T) ConfigLeanback.get(key, getDef(key));
|
|
||||||
}
|
|
||||||
/* Will never get here (IllegalArgumentException in getConfigType) */
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void set(String key, Object val) {
|
|
||||||
switch (getConfigType(key)) {
|
|
||||||
case PREF_INT:
|
|
||||||
getPrefs().edit().putInt(key, (int) val).apply();
|
|
||||||
break;
|
|
||||||
case PREF_STR_INT:
|
|
||||||
getPrefs().edit().putString(key, String.valueOf(val)).apply();
|
|
||||||
break;
|
|
||||||
case PREF_BOOL:
|
|
||||||
getPrefs().edit().putBoolean(key, (boolean) val).apply();
|
|
||||||
break;
|
|
||||||
case PREF_STR:
|
|
||||||
getPrefs().edit().putString(key, (String) val).apply();
|
|
||||||
break;
|
|
||||||
case DB_INT:
|
|
||||||
ConfigLeanback.put(key, (int) val);
|
|
||||||
break;
|
|
||||||
case DB_BOOL:
|
|
||||||
ConfigLeanback.put(key, (boolean) val ? 1 : 0);
|
|
||||||
break;
|
|
||||||
case DB_STR:
|
|
||||||
ConfigLeanback.put(key, (String) val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void remove(String key) {
|
|
||||||
switch (getConfigType(key)) {
|
|
||||||
case PREF_INT:
|
|
||||||
case PREF_STR_INT:
|
|
||||||
case PREF_BOOL:
|
|
||||||
case PREF_STR:
|
|
||||||
getPrefs().edit().remove(key).apply();
|
|
||||||
break;
|
|
||||||
case DB_BOOL:
|
|
||||||
case DB_INT:
|
|
||||||
ConfigLeanback.delete(key);
|
|
||||||
break;
|
|
||||||
case DB_STR:
|
|
||||||
ConfigLeanback.put(key, null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
/* Set default configurations */
|
|
||||||
|
|
||||||
// prefs int
|
|
||||||
defs.put(Key.REPO_ORDER, Value.ORDER_DATE);
|
|
||||||
|
|
||||||
// prefs string int
|
|
||||||
defs.put(Key.SU_REQUEST_TIMEOUT, 10);
|
|
||||||
defs.put(Key.SU_AUTO_RESPONSE, Value.SU_PROMPT);
|
|
||||||
defs.put(Key.SU_NOTIFICATION, Value.NOTIFICATION_TOAST);
|
|
||||||
defs.put(Key.UPDATE_CHANNEL, Utils.isCanary() ?
|
|
||||||
Value.CANARY_DEBUG_CHANNEL : Value.DEFAULT_CHANNEL);
|
|
||||||
|
|
||||||
// prefs bool
|
|
||||||
defs.put(Key.CHECK_UPDATES, true);
|
|
||||||
defs.put(Key.DARK_THEME, true);
|
|
||||||
//defs.put(Key.SU_REAUTH, false);
|
|
||||||
//defs.put(Key.SHOW_SYSTEM_APP, false);
|
|
||||||
|
|
||||||
// prefs string
|
|
||||||
defs.put(Key.CUSTOM_CHANNEL, "");
|
|
||||||
defs.put(Key.LOCALE, "");
|
|
||||||
//defs.put(Key.ETAG_KEY, null);
|
|
||||||
|
|
||||||
// db int
|
|
||||||
defs.put(Key.ROOT_ACCESS, Value.ROOT_ACCESS_APPS_AND_ADB);
|
|
||||||
defs.put(Key.SU_MNT_NS, Value.NAMESPACE_MODE_REQUESTER);
|
|
||||||
defs.put(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY);
|
|
||||||
|
|
||||||
// db bool
|
|
||||||
//defs.put(Key.SU_FINGERPRINT, false);
|
|
||||||
|
|
||||||
// db strings
|
|
||||||
//defs.put(Key.SU_MANAGER, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Nullable
|
|
||||||
private static <T> T getDef(String key) throws IllegalArgumentException {
|
|
||||||
Object val = defs.get(key);
|
|
||||||
switch (getConfigType(key)) {
|
|
||||||
case PREF_INT:
|
|
||||||
case DB_INT:
|
|
||||||
case PREF_STR_INT:
|
|
||||||
return val != null ? (T) val : (T) (Integer) 0;
|
|
||||||
case DB_BOOL:
|
|
||||||
case PREF_BOOL:
|
|
||||||
return val != null ? (T) val : (T) (Boolean) false;
|
|
||||||
case DB_STR:
|
|
||||||
case PREF_STR:
|
|
||||||
return (T) val;
|
|
||||||
}
|
|
||||||
/* Will never get here (IllegalArgumentException in getConfigType) */
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setDefs(SharedPreferences pref, SharedPreferences.Editor editor) {
|
|
||||||
for (String key : defs.keySet()) {
|
|
||||||
Object value = defs.get(key);
|
|
||||||
int type = getConfigType(key);
|
|
||||||
switch (type) {
|
|
||||||
case DB_INT:
|
|
||||||
editor.putString(key, String.valueOf(ConfigLeanback.get(key, (Integer) value)));
|
|
||||||
continue;
|
|
||||||
case DB_STR:
|
|
||||||
editor.putString(key, ConfigLeanback.get(key, String.valueOf(value)));
|
|
||||||
continue;
|
|
||||||
case DB_BOOL:
|
|
||||||
int bs = ConfigLeanback.get(key, -1);
|
|
||||||
editor.putBoolean(key, bs < 0 ? (Boolean) value : bs != 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (pref.contains(key))
|
|
||||||
continue;
|
|
||||||
switch (type) {
|
|
||||||
case PREF_INT:
|
|
||||||
editor.putInt(key, (Integer) value);
|
|
||||||
break;
|
|
||||||
case PREF_STR_INT:
|
|
||||||
editor.putString(key, String.valueOf(value));
|
|
||||||
break;
|
|
||||||
case PREF_STR:
|
|
||||||
editor.putString(key, (String) value);
|
|
||||||
break;
|
|
||||||
case PREF_BOOL:
|
|
||||||
editor.putBoolean(key, (Boolean) value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
196
app/src/main/java/com/topjohnwu/magisk/Config.kt
Normal file
196
app/src/main/java/com/topjohnwu/magisk/Config.kt
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
package com.topjohnwu.magisk
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Xml
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import com.topjohnwu.magisk.data.repository.DBConfig
|
||||||
|
import com.topjohnwu.magisk.data.database.SettingsDao
|
||||||
|
import com.topjohnwu.magisk.data.database.StringDao
|
||||||
|
import com.topjohnwu.magisk.di.Protected
|
||||||
|
import com.topjohnwu.magisk.model.preference.PreferenceModel
|
||||||
|
import com.topjohnwu.magisk.utils.*
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
|
import com.topjohnwu.superuser.io.SuFileInputStream
|
||||||
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object Config : PreferenceModel, DBConfig {
|
||||||
|
|
||||||
|
override val stringDao: StringDao by inject()
|
||||||
|
override val settingsDao: SettingsDao by inject()
|
||||||
|
override val context: Context by inject(Protected)
|
||||||
|
|
||||||
|
object Key {
|
||||||
|
// db configs
|
||||||
|
const val ROOT_ACCESS = "root_access"
|
||||||
|
const val SU_MULTIUSER_MODE = "multiuser_mode"
|
||||||
|
const val SU_MNT_NS = "mnt_ns"
|
||||||
|
const val SU_MANAGER = "requester"
|
||||||
|
const val SU_FINGERPRINT = "su_fingerprint"
|
||||||
|
|
||||||
|
// prefs
|
||||||
|
const val SU_REQUEST_TIMEOUT = "su_request_timeout"
|
||||||
|
const val SU_AUTO_RESPONSE = "su_auto_response"
|
||||||
|
const val SU_NOTIFICATION = "su_notification"
|
||||||
|
const val SU_REAUTH = "su_reauth"
|
||||||
|
const val CHECK_UPDATES = "check_update"
|
||||||
|
const val UPDATE_CHANNEL = "update_channel"
|
||||||
|
const val CUSTOM_CHANNEL = "custom_channel"
|
||||||
|
const val LOCALE = "locale"
|
||||||
|
const val DARK_THEME = "dark_theme"
|
||||||
|
const val ETAG_KEY = "ETag"
|
||||||
|
const val REPO_ORDER = "repo_order"
|
||||||
|
const val SHOW_SYSTEM_APP = "show_system"
|
||||||
|
|
||||||
|
// system state
|
||||||
|
const val MAGISKHIDE = "magiskhide"
|
||||||
|
const val COREONLY = "disable"
|
||||||
|
}
|
||||||
|
|
||||||
|
object Value {
|
||||||
|
// Update channels
|
||||||
|
const val DEFAULT_CHANNEL = -1
|
||||||
|
const val STABLE_CHANNEL = 0
|
||||||
|
const val BETA_CHANNEL = 1
|
||||||
|
const val CUSTOM_CHANNEL = 2
|
||||||
|
const val CANARY_CHANNEL = 3
|
||||||
|
const val CANARY_DEBUG_CHANNEL = 4
|
||||||
|
|
||||||
|
// root access mode
|
||||||
|
const val ROOT_ACCESS_DISABLED = 0
|
||||||
|
const val ROOT_ACCESS_APPS_ONLY = 1
|
||||||
|
const val ROOT_ACCESS_ADB_ONLY = 2
|
||||||
|
const val ROOT_ACCESS_APPS_AND_ADB = 3
|
||||||
|
|
||||||
|
// su multiuser
|
||||||
|
const val MULTIUSER_MODE_OWNER_ONLY = 0
|
||||||
|
const val MULTIUSER_MODE_OWNER_MANAGED = 1
|
||||||
|
const val MULTIUSER_MODE_USER = 2
|
||||||
|
|
||||||
|
// su mnt ns
|
||||||
|
const val NAMESPACE_MODE_GLOBAL = 0
|
||||||
|
const val NAMESPACE_MODE_REQUESTER = 1
|
||||||
|
const val NAMESPACE_MODE_ISOLATE = 2
|
||||||
|
|
||||||
|
// su notification
|
||||||
|
const val NO_NOTIFICATION = 0
|
||||||
|
const val NOTIFICATION_TOAST = 1
|
||||||
|
|
||||||
|
// su auto response
|
||||||
|
const val SU_PROMPT = 0
|
||||||
|
const val SU_AUTO_DENY = 1
|
||||||
|
const val SU_AUTO_ALLOW = 2
|
||||||
|
|
||||||
|
// su timeout
|
||||||
|
val TIMEOUT_LIST = intArrayOf(0, -1, 10, 20, 30, 60)
|
||||||
|
|
||||||
|
// repo order
|
||||||
|
const val ORDER_NAME = 0
|
||||||
|
const val ORDER_DATE = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private val defaultChannel =
|
||||||
|
if (Utils.isCanary) Value.CANARY_DEBUG_CHANNEL
|
||||||
|
else Value.DEFAULT_CHANNEL
|
||||||
|
|
||||||
|
var repoOrder by preference(Key.REPO_ORDER, Value.ORDER_DATE)
|
||||||
|
|
||||||
|
var suDefaultTimeout by preferenceStrInt(Key.SU_REQUEST_TIMEOUT, 10)
|
||||||
|
var suAutoReponse by preferenceStrInt(Key.SU_AUTO_RESPONSE, Value.SU_PROMPT)
|
||||||
|
var suNotification by preferenceStrInt(Key.SU_NOTIFICATION, Value.NOTIFICATION_TOAST)
|
||||||
|
var updateChannel by preferenceStrInt(Key.UPDATE_CHANNEL, defaultChannel)
|
||||||
|
|
||||||
|
var darkTheme by preference(Key.DARK_THEME, true)
|
||||||
|
var suReAuth by preference(Key.SU_REAUTH, false)
|
||||||
|
var checkUpdate by preference(Key.CHECK_UPDATES, true)
|
||||||
|
@JvmStatic
|
||||||
|
var magiskHide by preference(Key.MAGISKHIDE, true)
|
||||||
|
var coreOnly by preference(Key.COREONLY, false)
|
||||||
|
var showSystemApp by preference(Key.SHOW_SYSTEM_APP, false)
|
||||||
|
|
||||||
|
var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "")
|
||||||
|
var locale by preference(Key.LOCALE, "")
|
||||||
|
@JvmStatic
|
||||||
|
var etagKey by preference(Key.ETAG_KEY, "")
|
||||||
|
|
||||||
|
var rootMode by dbSettings(Key.ROOT_ACCESS, Value.ROOT_ACCESS_APPS_AND_ADB)
|
||||||
|
var suMntNamespaceMode by dbSettings(Key.SU_MNT_NS, Value.NAMESPACE_MODE_REQUESTER)
|
||||||
|
var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY)
|
||||||
|
var suFingerprint by dbSettings(Key.SU_FINGERPRINT, false)
|
||||||
|
@JvmStatic
|
||||||
|
var suManager by dbStrings(Key.SU_MANAGER, "")
|
||||||
|
|
||||||
|
fun initialize() = prefs.edit {
|
||||||
|
val config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS)
|
||||||
|
if (config.exists()) runCatching {
|
||||||
|
val input = SuFileInputStream(config).buffered()
|
||||||
|
val parser = Xml.newPullParser()
|
||||||
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
|
||||||
|
parser.setInput(input, "UTF-8")
|
||||||
|
parser.nextTag()
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "map")
|
||||||
|
while (parser.next() != XmlPullParser.END_TAG) {
|
||||||
|
if (parser.eventType != XmlPullParser.START_TAG)
|
||||||
|
continue
|
||||||
|
val key: String = parser.getAttributeValue(null, "name")
|
||||||
|
val value: String = parser.getAttributeValue(null, "value")
|
||||||
|
when (parser.name) {
|
||||||
|
"string" -> {
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "string")
|
||||||
|
putString(key, parser.nextText())
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "string")
|
||||||
|
}
|
||||||
|
"boolean" -> {
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "boolean")
|
||||||
|
putBoolean(key, value.toBoolean())
|
||||||
|
parser.nextTag()
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "boolean")
|
||||||
|
}
|
||||||
|
"int" -> {
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "int")
|
||||||
|
putInt(key, value.toInt())
|
||||||
|
parser.nextTag()
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "int")
|
||||||
|
}
|
||||||
|
"long" -> {
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "long")
|
||||||
|
putLong(key, value.toLong())
|
||||||
|
parser.nextTag()
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "long")
|
||||||
|
}
|
||||||
|
"float" -> {
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "int")
|
||||||
|
putFloat(key, value.toFloat())
|
||||||
|
parser.nextTag()
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "int")
|
||||||
|
}
|
||||||
|
else -> parser.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.delete()
|
||||||
|
}
|
||||||
|
remove(Key.ETAG_KEY)
|
||||||
|
if (!prefs.contains(Key.UPDATE_CHANNEL))
|
||||||
|
putString(Key.UPDATE_CHANNEL, defaultChannel.toString())
|
||||||
|
|
||||||
|
// Get actual state
|
||||||
|
putBoolean(Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
|
||||||
|
|
||||||
|
// Write database configs
|
||||||
|
putString(Key.ROOT_ACCESS, rootMode.toString())
|
||||||
|
putString(Key.SU_MNT_NS, suMntNamespaceMode.toString())
|
||||||
|
putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString())
|
||||||
|
putBoolean(Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint())
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun export() {
|
||||||
|
// Flush prefs to disk
|
||||||
|
prefs.edit().apply()
|
||||||
|
val xml = File("${get<Context>(Protected).filesDir.parent}/shared_prefs",
|
||||||
|
"${packageName}_preferences.xml")
|
||||||
|
Shell.su("cat $xml > /data/adb/${Const.MANAGER_CONFIGS}").exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,52 +0,0 @@
|
|||||||
package com.topjohnwu.magisk
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import androidx.annotation.AnyThread
|
|
||||||
import androidx.annotation.WorkerThread
|
|
||||||
import com.skoumal.teanity.extensions.subscribeK
|
|
||||||
import com.topjohnwu.magisk.data.repository.SettingRepository
|
|
||||||
import com.topjohnwu.magisk.data.repository.StringRepository
|
|
||||||
import com.topjohnwu.magisk.di.Protected
|
|
||||||
import com.topjohnwu.magisk.utils.inject
|
|
||||||
|
|
||||||
object ConfigLeanback {
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
val protectedContext: Context by inject(Protected)
|
|
||||||
@JvmStatic
|
|
||||||
val prefs: SharedPreferences by inject()
|
|
||||||
|
|
||||||
private val settingRepo: SettingRepository by inject()
|
|
||||||
private val stringRepo: StringRepository by inject()
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@AnyThread
|
|
||||||
fun put(key: String, value: Int) {
|
|
||||||
settingRepo.put(key, value).subscribeK()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@WorkerThread
|
|
||||||
fun get(key: String, defaultValue: Int): Int =
|
|
||||||
settingRepo.fetch(key, defaultValue).blockingGet()
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@AnyThread
|
|
||||||
fun put(key: String, value: String?) {
|
|
||||||
val task = value?.let { stringRepo.put(key, it) } ?: stringRepo.delete(key)
|
|
||||||
task.subscribeK()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@WorkerThread
|
|
||||||
fun get(key: String, defaultValue: String?): String =
|
|
||||||
stringRepo.fetch(key, defaultValue.orEmpty()).blockingGet()
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@AnyThread
|
|
||||||
fun delete(key: String) {
|
|
||||||
settingRepo.delete(key).subscribeK()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -19,7 +19,6 @@ object Const {
|
|||||||
const val MAGISK_LOG = "/cache/magisk.log"
|
const val MAGISK_LOG = "/cache/magisk.log"
|
||||||
|
|
||||||
// Versions
|
// Versions
|
||||||
const val UPDATE_SERVICE_VER = 1
|
|
||||||
const val SNET_EXT_VER = 12
|
const val SNET_EXT_VER = 12
|
||||||
const val SNET_REVISION = "b66b1a914978e5f4c4bbfd74a59f4ad371bac107"
|
const val SNET_REVISION = "b66b1a914978e5f4c4bbfd74a59f4ad371bac107"
|
||||||
const val BOOTCTL_REVISION = "9c5dfc1b8245c0b5b524901ef0ff0f8335757b77"
|
const val BOOTCTL_REVISION = "9c5dfc1b8245c0b5b524901ef0ff0f8335757b77"
|
||||||
|
36
app/src/main/java/com/topjohnwu/magisk/Info.java
Normal file
36
app/src/main/java/com/topjohnwu/magisk/Info.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
public final class Info {
|
||||||
|
|
||||||
|
public static int magiskVersionCode = -1;
|
||||||
|
// Current status
|
||||||
|
public static String magiskVersionString = "";
|
||||||
|
// Update Info
|
||||||
|
public static String remoteMagiskVersionString = "";
|
||||||
|
public static int remoteMagiskVersionCode = -1;
|
||||||
|
public static String magiskLink = "";
|
||||||
|
public static String magiskNoteLink = "";
|
||||||
|
public static String magiskMD5 = "";
|
||||||
|
public static String remoteManagerVersionString = "";
|
||||||
|
public static int remoteManagerVersionCode = -1;
|
||||||
|
public static String managerLink = "";
|
||||||
|
public static String managerNoteLink = "";
|
||||||
|
public static String uninstallerLink = "";
|
||||||
|
|
||||||
|
// Install flags
|
||||||
|
public static boolean keepVerity = false;
|
||||||
|
public static boolean keepEnc = false;
|
||||||
|
public static boolean recovery = false;
|
||||||
|
|
||||||
|
public static void loadMagiskInfo() {
|
||||||
|
try {
|
||||||
|
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
|
||||||
|
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
|
||||||
|
Config.setMagiskHide(Shell.su("magiskhide --status").exec().isSuccess());
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,46 +0,0 @@
|
|||||||
package com.topjohnwu.magisk
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.topjohnwu.magisk.KConfig.UpdateChannel.STABLE
|
|
||||||
import com.topjohnwu.magisk.di.Protected
|
|
||||||
import com.topjohnwu.magisk.model.preference.PreferenceModel
|
|
||||||
import com.topjohnwu.magisk.utils.inject
|
|
||||||
|
|
||||||
object KConfig : PreferenceModel {
|
|
||||||
|
|
||||||
override val context: Context by inject(Protected)
|
|
||||||
|
|
||||||
private var internalUpdateChannel by preference(Config.Key.UPDATE_CHANNEL, STABLE.id.toString())
|
|
||||||
var useCustomTabs by preference("useCustomTabs", true)
|
|
||||||
@JvmStatic
|
|
||||||
var customUpdateChannel by preference(Config.Key.CUSTOM_CHANNEL, "")
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
var updateChannel: UpdateChannel
|
|
||||||
get() = UpdateChannel.byId(internalUpdateChannel.toIntOrNull() ?: STABLE.id)
|
|
||||||
set(value) {
|
|
||||||
internalUpdateChannel = value.id.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal const val DEFAULT_CHANNEL = "topjohnwu/magisk_files"
|
|
||||||
|
|
||||||
enum class UpdateChannel(val id: Int) {
|
|
||||||
|
|
||||||
STABLE(Config.Value.STABLE_CHANNEL),
|
|
||||||
BETA(Config.Value.BETA_CHANNEL),
|
|
||||||
CANARY(Config.Value.CANARY_CHANNEL),
|
|
||||||
CANARY_DEBUG(Config.Value.CANARY_DEBUG_CHANNEL),
|
|
||||||
CUSTOM(Config.Value.CUSTOM_CHANNEL);
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun byId(id: Int) = when (id) {
|
|
||||||
Config.Value.STABLE_CHANNEL -> STABLE
|
|
||||||
Config.Value.BETA_CHANNEL -> BETA
|
|
||||||
Config.Value.CUSTOM_CHANNEL -> CUSTOM
|
|
||||||
Config.Value.CANARY_CHANNEL -> CANARY
|
|
||||||
Config.Value.CANARY_DEBUG_CHANNEL -> CANARY_DEBUG
|
|
||||||
else -> STABLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
package com.topjohnwu.magisk.data.database
|
package com.topjohnwu.magisk.data.database
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Config
|
|
||||||
import com.topjohnwu.magisk.data.database.base.*
|
import com.topjohnwu.magisk.data.database.base.*
|
||||||
import com.topjohnwu.magisk.model.entity.MagiskLog
|
import com.topjohnwu.magisk.model.entity.MagiskLog
|
||||||
import com.topjohnwu.magisk.model.entity.toLog
|
import com.topjohnwu.magisk.model.entity.toLog
|
||||||
@ -12,7 +11,7 @@ class LogDao : BaseDao() {
|
|||||||
override val table = DatabaseDefinition.Table.LOG
|
override val table = DatabaseDefinition.Table.LOG
|
||||||
|
|
||||||
fun deleteOutdated(
|
fun deleteOutdated(
|
||||||
suTimeout: Long = Config.suLogTimeout * TimeUnit.DAYS.toMillis(1)
|
suTimeout: Long = TimeUnit.DAYS.toMillis(14)
|
||||||
) = query<Delete> {
|
) = query<Delete> {
|
||||||
condition {
|
condition {
|
||||||
lessThan("time", suTimeout.toString())
|
lessThan("time", suTimeout.toString())
|
||||||
|
@ -1,118 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.data.database;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Config;
|
|
||||||
import com.topjohnwu.magisk.model.entity.Repo;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|
||||||
|
|
||||||
private static final int DATABASE_VER = 5;
|
|
||||||
private static final String TABLE_NAME = "repos";
|
|
||||||
|
|
||||||
private final SQLiteDatabase mDb;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public RepoDatabaseHelper(Context context) {
|
|
||||||
super(context, "repo.db", null, DATABASE_VER);
|
|
||||||
mDb = getWritableDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(SQLiteDatabase db) {
|
|
||||||
onUpgrade(db, 0, DATABASE_VER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
|
||||||
if (oldVersion != newVersion) {
|
|
||||||
// Nuke old DB and create new table
|
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
|
|
||||||
db.execSQL(
|
|
||||||
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
|
|
||||||
"(id TEXT, name TEXT, version TEXT, versionCode INT, " +
|
|
||||||
"author TEXT, description TEXT, last_update INT, PRIMARY KEY(id))");
|
|
||||||
Config.remove(Config.Key.ETAG_KEY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
|
||||||
onUpgrade(db, 0, DATABASE_VER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public void clearRepo() {
|
|
||||||
mDb.delete(TABLE_NAME, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public void removeRepo(String id) {
|
|
||||||
mDb.delete(TABLE_NAME, "id=?", new String[]{id});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public void removeRepo(Repo repo) {
|
|
||||||
removeRepo(repo.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public void removeRepo(Iterable<String> list) {
|
|
||||||
for (String id : list) {
|
|
||||||
if (id == null) continue;
|
|
||||||
mDb.delete(TABLE_NAME, "id=?", new String[]{id});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public void addRepo(Repo repo) {
|
|
||||||
mDb.replace(TABLE_NAME, null, repo.getContentValues());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public Repo getRepo(String id) {
|
|
||||||
try (Cursor c = mDb.query(TABLE_NAME, null, "id=?", new String[]{id}, null, null, null)) {
|
|
||||||
if (c.moveToNext()) {
|
|
||||||
return new Repo(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public Cursor getRawCursor() {
|
|
||||||
return mDb.query(TABLE_NAME, null, null, null, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public Cursor getRepoCursor() {
|
|
||||||
String orderBy = null;
|
|
||||||
switch ((int) Config.get(Config.Key.REPO_ORDER)) {
|
|
||||||
case Config.Value.ORDER_NAME:
|
|
||||||
orderBy = "name COLLATE NOCASE";
|
|
||||||
break;
|
|
||||||
case Config.Value.ORDER_DATE:
|
|
||||||
orderBy = "last_update DESC";
|
|
||||||
}
|
|
||||||
return mDb.query(TABLE_NAME, null, null, null, null, null, orderBy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public Set<String> getRepoIDSet() {
|
|
||||||
HashSet<String> set = new HashSet<>(300);
|
|
||||||
try (Cursor c = mDb.query(TABLE_NAME, null, null, null, null, null, null)) {
|
|
||||||
while (c.moveToNext()) {
|
|
||||||
set.add(c.getString(c.getColumnIndex("id")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,109 @@
|
|||||||
|
package com.topjohnwu.magisk.data.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import com.topjohnwu.magisk.Config
|
||||||
|
import com.topjohnwu.magisk.model.entity.Repo
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Deprecated("")
|
||||||
|
class RepoDatabaseHelper
|
||||||
|
constructor(context: Context) : SQLiteOpenHelper(context, "repo.db", null, DATABASE_VER) {
|
||||||
|
|
||||||
|
private val mDb: SQLiteDatabase = writableDatabase
|
||||||
|
|
||||||
|
val rawCursor: Cursor
|
||||||
|
@Deprecated("")
|
||||||
|
get() = mDb.query(TABLE_NAME, null, null, null, null, null, null)
|
||||||
|
|
||||||
|
val repoCursor: Cursor
|
||||||
|
@Deprecated("")
|
||||||
|
get() {
|
||||||
|
var orderBy: String? = null
|
||||||
|
when (Config.repoOrder) {
|
||||||
|
Config.Value.ORDER_NAME -> orderBy = "name COLLATE NOCASE"
|
||||||
|
Config.Value.ORDER_DATE -> orderBy = "last_update DESC"
|
||||||
|
}
|
||||||
|
return mDb.query(TABLE_NAME, null, null, null, null, null, orderBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
val repoIDSet: Set<String>
|
||||||
|
@Deprecated("")
|
||||||
|
get() {
|
||||||
|
val set = HashSet<String>(300)
|
||||||
|
mDb.query(TABLE_NAME, null, null, null, null, null, null).use { c ->
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
set.add(c.getString(c.getColumnIndex("id")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
|
onUpgrade(db, 0, DATABASE_VER)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
if (oldVersion != newVersion) {
|
||||||
|
// Nuke old DB and create new table
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS $TABLE_NAME")
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
|
||||||
|
"(id TEXT, name TEXT, version TEXT, versionCode INT, " +
|
||||||
|
"author TEXT, description TEXT, last_update INT, PRIMARY KEY(id))")
|
||||||
|
Config.prefs.edit {
|
||||||
|
remove(Config.Key.ETAG_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
onUpgrade(db, 0, DATABASE_VER)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("")
|
||||||
|
fun clearRepo() {
|
||||||
|
mDb.delete(TABLE_NAME, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated("")
|
||||||
|
fun removeRepo(id: String) {
|
||||||
|
mDb.delete(TABLE_NAME, "id=?", arrayOf(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("")
|
||||||
|
fun removeRepo(repo: Repo) {
|
||||||
|
removeRepo(repo.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("")
|
||||||
|
fun removeRepo(list: Iterable<String>) {
|
||||||
|
list.forEach {
|
||||||
|
mDb.delete(TABLE_NAME, "id=?", arrayOf(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("")
|
||||||
|
fun addRepo(repo: Repo) {
|
||||||
|
mDb.replace(TABLE_NAME, null, repo.contentValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("")
|
||||||
|
fun getRepo(id: String): Repo? {
|
||||||
|
mDb.query(TABLE_NAME, null, "id=?", arrayOf(id), null, null, null).use { c ->
|
||||||
|
if (c.moveToNext()) {
|
||||||
|
return Repo(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val DATABASE_VER = 5
|
||||||
|
private val TABLE_NAME = "repos"
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.data.network
|
package com.topjohnwu.magisk.data.network
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const
|
import com.topjohnwu.magisk.Const
|
||||||
import com.topjohnwu.magisk.KConfig
|
|
||||||
import com.topjohnwu.magisk.model.entity.MagiskConfig
|
import com.topjohnwu.magisk.model.entity.MagiskConfig
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
@ -16,19 +15,19 @@ interface GithubRawApiServices {
|
|||||||
//region topjohnwu/magisk_files
|
//region topjohnwu/magisk_files
|
||||||
|
|
||||||
@GET("$MAGISK_FILES/master/stable.json")
|
@GET("$MAGISK_FILES/master/stable.json")
|
||||||
fun fetchConfig(): Single<MagiskConfig>
|
fun fetchStableUpdate(): Single<MagiskConfig>
|
||||||
|
|
||||||
@GET("$MAGISK_FILES/master/beta.json")
|
@GET("$MAGISK_FILES/master/beta.json")
|
||||||
fun fetchBetaConfig(): Single<MagiskConfig>
|
fun fetchBetaUpdate(): Single<MagiskConfig>
|
||||||
|
|
||||||
@GET("$MAGISK_FILES/master/canary_builds/release.json")
|
@GET("$MAGISK_FILES/master/canary_builds/release.json")
|
||||||
fun fetchCanaryConfig(): Single<MagiskConfig>
|
fun fetchCanaryUpdate(): Single<MagiskConfig>
|
||||||
|
|
||||||
@GET("$MAGISK_FILES/master/canary_builds/canary.json")
|
@GET("$MAGISK_FILES/master/canary_builds/canary.json")
|
||||||
fun fetchCanaryDebugConfig(): Single<MagiskConfig>
|
fun fetchCanaryDebugUpdate(): Single<MagiskConfig>
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
fun fetchCustomConfig(@Url url: String): Single<MagiskConfig>
|
fun fetchCustomUpdate(@Url url: String): Single<MagiskConfig>
|
||||||
|
|
||||||
@GET("$MAGISK_FILES/{$REVISION}/snet.apk")
|
@GET("$MAGISK_FILES/{$REVISION}/snet.apk")
|
||||||
@Streaming
|
@Streaming
|
||||||
@ -55,7 +54,7 @@ interface GithubRawApiServices {
|
|||||||
private const val FILE = "file"
|
private const val FILE = "file"
|
||||||
|
|
||||||
|
|
||||||
private const val MAGISK_FILES = KConfig.DEFAULT_CHANNEL
|
private const val MAGISK_FILES = "topjohnwu/magisk_files"
|
||||||
private const val MAGISK_MASTER = "topjohnwu/Magisk/master"
|
private const val MAGISK_MASTER = "topjohnwu/Magisk/master"
|
||||||
private const val MAGISK_MODULES = "Magisk-Modules-Repo"
|
private const val MAGISK_MODULES = "Magisk-Modules-Repo"
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
package com.topjohnwu.magisk.data.repository
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.data.database.SettingsDao
|
||||||
|
import com.topjohnwu.magisk.data.database.StringDao
|
||||||
|
import com.topjohnwu.magisk.utils.trimEmptyToNull
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
interface DBConfig {
|
||||||
|
val settingsDao: SettingsDao
|
||||||
|
val stringDao: StringDao
|
||||||
|
|
||||||
|
fun dbSettings(
|
||||||
|
name: String,
|
||||||
|
default: Int
|
||||||
|
) = DBSettingsValue(name, default)
|
||||||
|
|
||||||
|
fun dbSettings(
|
||||||
|
name: String,
|
||||||
|
default: Boolean
|
||||||
|
) = DBBoolSettings(name, default)
|
||||||
|
|
||||||
|
fun dbStrings(
|
||||||
|
name: String,
|
||||||
|
default: String
|
||||||
|
) = DBStringsValue(name, default)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class DBSettingsValue(
|
||||||
|
private val name: String,
|
||||||
|
private val default: Int
|
||||||
|
) : ReadWriteProperty<DBConfig, Int> {
|
||||||
|
|
||||||
|
private var value: Int? = null
|
||||||
|
|
||||||
|
private fun getKey(property: KProperty<*>) = name.trimEmptyToNull() ?: property.name
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getValue(thisRef: DBConfig, property: KProperty<*>): Int {
|
||||||
|
if (value == null)
|
||||||
|
value = thisRef.settingsDao.fetch(getKey(property), default).blockingGet()
|
||||||
|
return value!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: DBConfig, property: KProperty<*>, value: Int) {
|
||||||
|
synchronized(this) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
thisRef.settingsDao.put(getKey(property), value)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DBBoolSettings(
|
||||||
|
name: String,
|
||||||
|
default: Boolean
|
||||||
|
) : ReadWriteProperty<DBConfig, Boolean> {
|
||||||
|
|
||||||
|
val base = DBSettingsValue(name, if (default) 1 else 0)
|
||||||
|
|
||||||
|
override fun getValue(thisRef: DBConfig, property: KProperty<*>): Boolean
|
||||||
|
= base.getValue(thisRef, property) != 0
|
||||||
|
|
||||||
|
override fun setValue(thisRef: DBConfig, property: KProperty<*>, value: Boolean) =
|
||||||
|
base.setValue(thisRef, property, if (value) 1 else 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
class DBStringsValue(
|
||||||
|
private val name: String,
|
||||||
|
private val default: String
|
||||||
|
) : ReadWriteProperty<DBConfig, String> {
|
||||||
|
|
||||||
|
private var value: String? = null
|
||||||
|
|
||||||
|
private fun getKey(property: KProperty<*>) = name.trimEmptyToNull() ?: property.name
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getValue(thisRef: DBConfig, property: KProperty<*>): String {
|
||||||
|
if (value == null)
|
||||||
|
value = thisRef.stringDao.fetch(getKey(property), default).blockingGet()
|
||||||
|
return value!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: DBConfig, property: KProperty<*>, value: String) {
|
||||||
|
synchronized(this) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
thisRef.stringDao.put(getKey(property), value)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe()
|
||||||
|
}
|
||||||
|
}
|
@ -4,20 +4,17 @@ import android.content.Context
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import com.topjohnwu.magisk.App
|
import com.topjohnwu.magisk.App
|
||||||
import com.topjohnwu.magisk.Config
|
import com.topjohnwu.magisk.Config
|
||||||
import com.topjohnwu.magisk.KConfig
|
import com.topjohnwu.magisk.Info
|
||||||
import com.topjohnwu.magisk.data.database.base.su
|
import com.topjohnwu.magisk.data.database.base.su
|
||||||
import com.topjohnwu.magisk.data.database.base.suRaw
|
|
||||||
import com.topjohnwu.magisk.data.network.GithubRawApiServices
|
import com.topjohnwu.magisk.data.network.GithubRawApiServices
|
||||||
import com.topjohnwu.magisk.model.entity.HideAppInfo
|
import com.topjohnwu.magisk.model.entity.HideAppInfo
|
||||||
import com.topjohnwu.magisk.model.entity.HideTarget
|
import com.topjohnwu.magisk.model.entity.HideTarget
|
||||||
import com.topjohnwu.magisk.model.entity.Version
|
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.magisk.utils.inject
|
import com.topjohnwu.magisk.utils.inject
|
||||||
import com.topjohnwu.magisk.utils.toSingle
|
import com.topjohnwu.magisk.utils.toSingle
|
||||||
import com.topjohnwu.magisk.utils.writeToFile
|
import com.topjohnwu.magisk.utils.writeToFile
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.functions.BiFunction
|
|
||||||
|
|
||||||
class MagiskRepository(
|
class MagiskRepository(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
@ -25,15 +22,15 @@ class MagiskRepository(
|
|||||||
private val packageManager: PackageManager
|
private val packageManager: PackageManager
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun fetchMagisk() = fetchConfig()
|
fun fetchMagisk() = fetchUpdate()
|
||||||
.flatMap { apiRaw.fetchFile(it.magisk.link) }
|
.flatMap { apiRaw.fetchFile(it.magisk.link) }
|
||||||
.map { it.writeToFile(context, FILE_MAGISK_ZIP) }
|
.map { it.writeToFile(context, FILE_MAGISK_ZIP) }
|
||||||
|
|
||||||
fun fetchManager() = fetchConfig()
|
fun fetchManager() = fetchUpdate()
|
||||||
.flatMap { apiRaw.fetchFile(it.app.link) }
|
.flatMap { apiRaw.fetchFile(it.app.link) }
|
||||||
.map { it.writeToFile(context, FILE_MAGISK_APK) }
|
.map { it.writeToFile(context, FILE_MAGISK_APK) }
|
||||||
|
|
||||||
fun fetchUninstaller() = fetchConfig()
|
fun fetchUninstaller() = fetchUpdate()
|
||||||
.flatMap { apiRaw.fetchFile(it.uninstaller.link) }
|
.flatMap { apiRaw.fetchFile(it.uninstaller.link) }
|
||||||
.map { it.writeToFile(context, FILE_UNINSTALLER_ZIP) }
|
.map { it.writeToFile(context, FILE_UNINSTALLER_ZIP) }
|
||||||
|
|
||||||
@ -44,23 +41,35 @@ class MagiskRepository(
|
|||||||
.map { it.writeToFile(context, FILE_BOOTCTL_SH) }
|
.map { it.writeToFile(context, FILE_BOOTCTL_SH) }
|
||||||
|
|
||||||
|
|
||||||
fun fetchConfig() = when (KConfig.updateChannel) {
|
fun fetchUpdate() = when (Config.updateChannel) {
|
||||||
KConfig.UpdateChannel.STABLE -> apiRaw.fetchConfig()
|
Config.Value.DEFAULT_CHANNEL, Config.Value.STABLE_CHANNEL -> apiRaw.fetchStableUpdate()
|
||||||
KConfig.UpdateChannel.BETA -> apiRaw.fetchBetaConfig()
|
Config.Value.BETA_CHANNEL -> apiRaw.fetchBetaUpdate()
|
||||||
KConfig.UpdateChannel.CANARY -> apiRaw.fetchCanaryConfig()
|
Config.Value.CANARY_CHANNEL -> apiRaw.fetchCanaryUpdate()
|
||||||
KConfig.UpdateChannel.CANARY_DEBUG -> apiRaw.fetchCanaryDebugConfig()
|
Config.Value.CANARY_DEBUG_CHANNEL -> apiRaw.fetchCanaryDebugUpdate()
|
||||||
KConfig.UpdateChannel.CUSTOM -> apiRaw.fetchCustomConfig(KConfig.customUpdateChannel)
|
Config.Value.CUSTOM_CHANNEL -> apiRaw.fetchCustomUpdate(Config.customChannelUrl)
|
||||||
}
|
else -> throw IllegalArgumentException()
|
||||||
.doOnSuccess {
|
}.flatMap {
|
||||||
Config.remoteMagiskVersionCode = it.magisk.versionCode.toIntOrNull() ?: -1
|
// If remote version is lower than current installed, try switching to beta
|
||||||
Config.magiskLink = it.magisk.link
|
if (it.magisk.versionCode.toIntOrNull() ?: -1 < Info.magiskVersionCode
|
||||||
Config.magiskNoteLink = it.magisk.note
|
&& Config.updateChannel == Config.Value.DEFAULT_CHANNEL) {
|
||||||
Config.magiskMD5 = it.magisk.hash
|
Config.updateChannel = Config.Value.BETA_CHANNEL
|
||||||
Config.remoteManagerVersionCode = it.app.versionCode.toIntOrNull() ?: -1
|
apiRaw.fetchBetaUpdate()
|
||||||
Config.remoteManagerVersionString = it.app.version
|
} else {
|
||||||
Config.managerLink = it.app.link
|
Single.just(it)
|
||||||
Config.managerNoteLink = it.app.note
|
}
|
||||||
Config.uninstallerLink = it.uninstaller.link
|
}.doOnSuccess {
|
||||||
|
Info.remoteMagiskVersionString = it.magisk.version
|
||||||
|
Info.remoteMagiskVersionCode = it.magisk.versionCode.toIntOrNull() ?: -1
|
||||||
|
Info.magiskLink = it.magisk.link
|
||||||
|
Info.magiskNoteLink = it.magisk.note
|
||||||
|
Info.magiskMD5 = it.magisk.hash
|
||||||
|
|
||||||
|
Info.remoteManagerVersionString = it.app.version
|
||||||
|
Info.remoteManagerVersionCode = it.app.versionCode.toIntOrNull() ?: -1
|
||||||
|
Info.managerLink = it.app.link
|
||||||
|
Info.managerNoteLink = it.app.note
|
||||||
|
|
||||||
|
Info.uninstallerLink = it.uninstaller.link
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchApps() =
|
fun fetchApps() =
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.data.repository
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.data.database.SettingsDao
|
|
||||||
|
|
||||||
class SettingRepository(private val settingsDao: SettingsDao) {
|
|
||||||
|
|
||||||
fun fetch(key: String, default: Int) = settingsDao.fetch(key, default)
|
|
||||||
fun put(key: String, value: Int) = settingsDao.put(key, value)
|
|
||||||
fun delete(key: String) = settingsDao.delete(key)
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.data.repository
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.data.database.StringDao
|
|
||||||
|
|
||||||
class StringRepository(private val stringDao: StringDao) {
|
|
||||||
|
|
||||||
fun fetch(key: String, default: String) = stringDao.fetch(key, default)
|
|
||||||
fun put(key: String, value: String) = stringDao.put(key, value)
|
|
||||||
fun delete(key: String) = stringDao.delete(key)
|
|
||||||
|
|
||||||
}
|
|
@ -8,6 +8,4 @@ val repositoryModule = module {
|
|||||||
single { MagiskRepository(get(), get(), get()) }
|
single { MagiskRepository(get(), get(), get()) }
|
||||||
single { LogRepository(get()) }
|
single { LogRepository(get()) }
|
||||||
single { AppRepository(get()) }
|
single { AppRepository(get()) }
|
||||||
single { SettingRepository(get()) }
|
|
||||||
single { StringRepository(get()) }
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import android.content.Intent;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
import com.topjohnwu.magisk.App;
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
import com.topjohnwu.magisk.ClassMap;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
@ -29,8 +31,6 @@ import java.util.zip.ZipEntry;
|
|||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
public class DownloadModuleService extends Service {
|
public class DownloadModuleService extends Service {
|
||||||
|
|
||||||
private List<ProgressNotification> notifications;
|
private List<ProgressNotification> notifications;
|
||||||
|
@ -5,10 +5,10 @@ import android.database.Cursor;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class BaseModule implements Comparable<BaseModule>, Parcelable {
|
public abstract class BaseModule implements Comparable<BaseModule>, Parcelable {
|
||||||
|
|
||||||
private String mId, mName, mVersion, mAuthor, mDescription;
|
private String mId, mName, mVersion, mAuthor, mDescription;
|
||||||
|
@ -4,10 +4,10 @@ import android.content.ContentValues;
|
|||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
|
||||||
public class Policy implements Comparable<Policy>{
|
public class Policy implements Comparable<Policy>{
|
||||||
public static final int INTERACTIVE = 0;
|
public static final int INTERACTIVE = 0;
|
||||||
@ -27,7 +27,7 @@ public class Policy implements Comparable<Policy>{
|
|||||||
this.uid = uid;
|
this.uid = uid;
|
||||||
packageName = pkgs[0];
|
packageName = pkgs[0];
|
||||||
info = pm.getApplicationInfo(packageName, 0);
|
info = pm.getApplicationInfo(packageName, 0);
|
||||||
appName = Utils.getAppLabel(info, pm);
|
appName = Utils.INSTANCE.getAppLabel(info, pm);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Policy(ContentValues values, PackageManager pm) throws PackageManager.NameNotFoundException {
|
public Policy(ContentValues values, PackageManager pm) throws PackageManager.NameNotFoundException {
|
||||||
|
@ -54,7 +54,7 @@ public class Repo extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update() throws IllegalRepoException {
|
public void update() throws IllegalRepoException {
|
||||||
String[] props = Utils.dlString(getPropUrl()).split("\\n");
|
String[] props = Utils.INSTANCE.dlString(getPropUrl()).split("\\n");
|
||||||
try {
|
try {
|
||||||
parseProps(props);
|
parseProps(props);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
@ -103,7 +103,7 @@ public class Repo extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getDownloadFilename() {
|
public String getDownloadFilename() {
|
||||||
return Utils.getLegalFilename(getName() + "-" + getVersion() + ".zip");
|
return Utils.INSTANCE.getLegalFilename(getName() + "-" + getVersion() + ".zip");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IllegalRepoException extends Exception {
|
public class IllegalRepoException extends Exception {
|
||||||
|
@ -2,6 +2,8 @@ package com.topjohnwu.magisk.model.preference
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
interface PreferenceModel {
|
interface PreferenceModel {
|
||||||
|
|
||||||
@ -14,6 +16,20 @@ interface PreferenceModel {
|
|||||||
val prefs: SharedPreferences
|
val prefs: SharedPreferences
|
||||||
get() = context.getSharedPreferences(fileName, Context.MODE_PRIVATE)
|
get() = context.getSharedPreferences(fileName, Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
fun preferenceStrInt(
|
||||||
|
name: String,
|
||||||
|
default: Int,
|
||||||
|
writeDefault: Boolean = false,
|
||||||
|
commit: Boolean = commitPrefs
|
||||||
|
) = object: ReadWriteProperty<PreferenceModel, Int> {
|
||||||
|
val base = StringProperty(name, default.toString(), commit)
|
||||||
|
override fun getValue(thisRef: PreferenceModel, property: KProperty<*>): Int =
|
||||||
|
base.getValue(thisRef, property).toInt()
|
||||||
|
|
||||||
|
override fun setValue(thisRef: PreferenceModel, property: KProperty<*>, value: Int) =
|
||||||
|
base.setValue(thisRef, property, value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
fun preference(
|
fun preference(
|
||||||
name: String,
|
name: String,
|
||||||
default: Boolean,
|
default: Boolean,
|
||||||
|
@ -6,6 +6,7 @@ import android.content.Intent
|
|||||||
import com.topjohnwu.magisk.ClassMap
|
import com.topjohnwu.magisk.ClassMap
|
||||||
import com.topjohnwu.magisk.Config
|
import com.topjohnwu.magisk.Config
|
||||||
import com.topjohnwu.magisk.Const
|
import com.topjohnwu.magisk.Const
|
||||||
|
import com.topjohnwu.magisk.Info
|
||||||
import com.topjohnwu.magisk.data.database.base.su
|
import com.topjohnwu.magisk.data.database.base.su
|
||||||
import com.topjohnwu.magisk.data.repository.AppRepository
|
import com.topjohnwu.magisk.data.repository.AppRepository
|
||||||
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||||
@ -13,7 +14,6 @@ import com.topjohnwu.magisk.utils.DownloadApp
|
|||||||
import com.topjohnwu.magisk.utils.RootUtils
|
import com.topjohnwu.magisk.utils.RootUtils
|
||||||
import com.topjohnwu.magisk.utils.SuLogger
|
import com.topjohnwu.magisk.utils.SuLogger
|
||||||
import com.topjohnwu.magisk.utils.inject
|
import com.topjohnwu.magisk.utils.inject
|
||||||
import com.topjohnwu.magisk.utils.get
|
|
||||||
import com.topjohnwu.magisk.view.Notifications
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
import com.topjohnwu.magisk.view.Shortcuts
|
import com.topjohnwu.magisk.view.Shortcuts
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
@ -64,9 +64,8 @@ open class GeneralReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
Intent.ACTION_PACKAGE_REPLACED ->
|
Intent.ACTION_PACKAGE_REPLACED ->
|
||||||
// This will only work pre-O
|
// This will only work pre-O
|
||||||
if (Config.get<Boolean>(Config.Key.SU_REAUTH)!!) {
|
if (Config.suReAuth)
|
||||||
appRepo.delete(getPkg(intent)).blockingGet()
|
appRepo.delete(getPkg(intent)).blockingGet()
|
||||||
}
|
|
||||||
Intent.ACTION_PACKAGE_FULLY_REMOVED -> {
|
Intent.ACTION_PACKAGE_FULLY_REMOVED -> {
|
||||||
val pkg = getPkg(intent)
|
val pkg = getPkg(intent)
|
||||||
appRepo.delete(pkg).blockingGet()
|
appRepo.delete(pkg).blockingGet()
|
||||||
@ -74,7 +73,7 @@ open class GeneralReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setup(context)
|
Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setup(context)
|
||||||
Const.Key.BROADCAST_MANAGER_UPDATE -> {
|
Const.Key.BROADCAST_MANAGER_UPDATE -> {
|
||||||
Config.managerLink = intent.getStringExtra(Const.Key.INTENT_SET_LINK)
|
Info.managerLink = intent.getStringExtra(Const.Key.INTENT_SET_LINK)
|
||||||
DownloadApp.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME))
|
DownloadApp.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME))
|
||||||
}
|
}
|
||||||
Const.Key.BROADCAST_REBOOT -> RootUtils.reboot()
|
Const.Key.BROADCAST_REBOOT -> RootUtils.reboot()
|
||||||
|
@ -2,7 +2,7 @@ package com.topjohnwu.magisk.model.update
|
|||||||
|
|
||||||
import androidx.work.ListenableWorker
|
import androidx.work.ListenableWorker
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
import com.topjohnwu.magisk.Config
|
import com.topjohnwu.magisk.Info
|
||||||
import com.topjohnwu.magisk.data.repository.MagiskRepository
|
import com.topjohnwu.magisk.data.repository.MagiskRepository
|
||||||
import com.topjohnwu.magisk.model.worker.DelegateWorker
|
import com.topjohnwu.magisk.model.worker.DelegateWorker
|
||||||
import com.topjohnwu.magisk.utils.inject
|
import com.topjohnwu.magisk.utils.inject
|
||||||
@ -14,10 +14,10 @@ class UpdateCheckService : DelegateWorker() {
|
|||||||
|
|
||||||
override fun doWork(): ListenableWorker.Result {
|
override fun doWork(): ListenableWorker.Result {
|
||||||
return runCatching {
|
return runCatching {
|
||||||
magiskRepo.fetchConfig().blockingGet()
|
magiskRepo.fetchUpdate().blockingGet()
|
||||||
if (BuildConfig.VERSION_CODE < Config.remoteManagerVersionCode)
|
if (BuildConfig.VERSION_CODE < Info.remoteManagerVersionCode)
|
||||||
Notifications.managerUpdate()
|
Notifications.managerUpdate()
|
||||||
else if (Config.magiskVersionCode < Config.remoteManagerVersionCode)
|
else if (Info.magiskVersionCode < Info.remoteManagerVersionCode)
|
||||||
Notifications.magiskUpdate()
|
Notifications.magiskUpdate()
|
||||||
ListenableWorker.Result.success()
|
ListenableWorker.Result.success()
|
||||||
}.getOrElse {
|
}.getOrElse {
|
||||||
|
@ -4,12 +4,6 @@ import android.content.Context;
|
|||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import androidx.annotation.MainThread;
|
import androidx.annotation.MainThread;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -17,6 +11,12 @@ import androidx.annotation.RequiresApi;
|
|||||||
import androidx.work.Data;
|
import androidx.work.Data;
|
||||||
import androidx.work.ListenableWorker;
|
import androidx.work.ListenableWorker;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public abstract class DelegateWorker {
|
public abstract class DelegateWorker {
|
||||||
|
|
||||||
private ListenableWorker worker;
|
private ListenableWorker worker;
|
||||||
|
@ -8,8 +8,8 @@ import androidx.annotation.MainThread;
|
|||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
import com.topjohnwu.magisk.App;
|
||||||
import com.topjohnwu.magisk.Config;
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Info;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.net.DownloadProgressListener;
|
import com.topjohnwu.net.DownloadProgressListener;
|
||||||
import com.topjohnwu.net.Networking;
|
import com.topjohnwu.net.Networking;
|
||||||
@ -123,9 +123,9 @@ public abstract class MagiskInstaller {
|
|||||||
|
|
||||||
File zip = new File(App.self.getCacheDir(), "magisk.zip");
|
File zip = new File(App.self.getCacheDir(), "magisk.zip");
|
||||||
|
|
||||||
if (!ShellUtils.checkSum("MD5", zip, Config.magiskMD5)) {
|
if (!ShellUtils.checkSum("MD5", zip, Info.magiskMD5)) {
|
||||||
console.add("- Downloading zip");
|
console.add("- Downloading zip");
|
||||||
Networking.get(Config.magiskLink)
|
Networking.get(Info.magiskLink)
|
||||||
.setDownloadProgressListener(new ProgressLog())
|
.setDownloadProgressListener(new ProgressLog())
|
||||||
.execForFile(zip);
|
.execForFile(zip);
|
||||||
} else {
|
} else {
|
||||||
@ -282,10 +282,10 @@ public abstract class MagiskInstaller {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Shell.sh(Utils.fmt(
|
if (!Shell.sh(Utils.INSTANCE.fmt(
|
||||||
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b RECOVERYMODE=%b " +
|
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b RECOVERYMODE=%b " +
|
||||||
"sh update-binary sh boot_patch.sh %s",
|
"sh update-binary sh boot_patch.sh %s",
|
||||||
Config.keepEnc, Config.keepVerity, Config.recovery, srcBoot))
|
Info.keepEnc, Info.keepVerity, Info.recovery, srcBoot))
|
||||||
.to(console, logs).exec().isSuccess())
|
.to(console, logs).exec().isSuccess())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -311,10 +311,10 @@ public abstract class MagiskInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean flashBoot() {
|
protected boolean flashBoot() {
|
||||||
if (!Shell.su(Utils.fmt("direct_install %s %s", installDir, srcBoot))
|
if (!Shell.su(Utils.INSTANCE.fmt("direct_install %s %s", installDir, srcBoot))
|
||||||
.to(console, logs).exec().isSuccess())
|
.to(console, logs).exec().isSuccess())
|
||||||
return false;
|
return false;
|
||||||
if (!Config.keepVerity)
|
if (!Info.keepVerity)
|
||||||
Shell.su("patch_dtbo_image").to(console, logs).exec();
|
Shell.su("patch_dtbo_image").to(console, logs).exec();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package com.topjohnwu.magisk.tasks;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
import com.topjohnwu.magisk.App;
|
||||||
import com.topjohnwu.magisk.Config;
|
import com.topjohnwu.magisk.Config;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
@ -31,7 +33,6 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -74,10 +75,10 @@ public class UpdateRepos {
|
|||||||
* first page is updated to determine whether the online repo database is changed
|
* first page is updated to determine whether the online repo database is changed
|
||||||
*/
|
*/
|
||||||
private boolean parsePage(int page) {
|
private boolean parsePage(int page) {
|
||||||
Request req = Networking.get(Utils.fmt(Const.Url.REPO_URL, page + 1));
|
Request req = Networking.get(Utils.INSTANCE.fmt(Const.Url.REPO_URL, page + 1));
|
||||||
if (page == 0) {
|
if (page == 0) {
|
||||||
String etag = Config.get(Config.Key.ETAG_KEY);
|
String etag = Config.getEtagKey();
|
||||||
if (etag != null)
|
if (!etag.isEmpty())
|
||||||
req.addHeaders(Const.Key.IF_NONE_MATCH, etag);
|
req.addHeaders(Const.Key.IF_NONE_MATCH, etag);
|
||||||
}
|
}
|
||||||
Request.Result<JSONArray> res = req.execForJSONArray();
|
Request.Result<JSONArray> res = req.execForJSONArray();
|
||||||
@ -110,7 +111,7 @@ public class UpdateRepos {
|
|||||||
String etag = res.getConnection().getHeaderField(Config.Key.ETAG_KEY);
|
String etag = res.getConnection().getHeaderField(Config.Key.ETAG_KEY);
|
||||||
if (etag != null) {
|
if (etag != null) {
|
||||||
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
||||||
Config.set(Config.Key.ETAG_KEY, etag);
|
Config.setEtagKey(etag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import androidx.fragment.app.Fragment
|
|||||||
import com.topjohnwu.magisk.ClassMap
|
import com.topjohnwu.magisk.ClassMap
|
||||||
import com.topjohnwu.magisk.Config
|
import com.topjohnwu.magisk.Config
|
||||||
import com.topjohnwu.magisk.Const.Key.OPEN_SECTION
|
import com.topjohnwu.magisk.Const.Key.OPEN_SECTION
|
||||||
|
import com.topjohnwu.magisk.Info
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.databinding.ActivityMainBinding
|
import com.topjohnwu.magisk.databinding.ActivityMainBinding
|
||||||
import com.topjohnwu.magisk.model.navigation.Navigation
|
import com.topjohnwu.magisk.model.navigation.Navigation
|
||||||
@ -105,11 +106,11 @@ open class MainActivity : MagiskActivity<MainViewModel, ActivityMainBinding>() {
|
|||||||
private fun checkHideSection() {
|
private fun checkHideSection() {
|
||||||
val menu = binding.navView.menu
|
val menu = binding.navView.menu
|
||||||
menu.findItem(R.id.magiskHideFragment).isVisible =
|
menu.findItem(R.id.magiskHideFragment).isVisible =
|
||||||
Shell.rootAccess() && Config.get<Any>(Config.Key.MAGISKHIDE) as Boolean
|
Shell.rootAccess() && Config.magiskHide
|
||||||
menu.findItem(R.id.modulesFragment).isVisible =
|
menu.findItem(R.id.modulesFragment).isVisible =
|
||||||
Shell.rootAccess() && Config.magiskVersionCode >= 0
|
Shell.rootAccess() && Info.magiskVersionCode >= 0
|
||||||
menu.findItem(R.id.reposFragment).isVisible =
|
menu.findItem(R.id.reposFragment).isVisible =
|
||||||
(Networking.checkNetworkStatus(this) && Shell.rootAccess() && Config.magiskVersionCode >= 0)
|
(Networking.checkNetworkStatus(this) && Shell.rootAccess() && Info.magiskVersionCode >= 0)
|
||||||
menu.findItem(R.id.logFragment).isVisible =
|
menu.findItem(R.id.logFragment).isVisible =
|
||||||
Shell.rootAccess()
|
Shell.rootAccess()
|
||||||
menu.findItem(R.id.superuserFragment).isVisible =
|
menu.findItem(R.id.superuserFragment).isVisible =
|
||||||
|
@ -7,6 +7,7 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.skoumal.teanity.extensions.subscribeK
|
import com.skoumal.teanity.extensions.subscribeK
|
||||||
import com.topjohnwu.magisk.*
|
import com.topjohnwu.magisk.*
|
||||||
|
import com.topjohnwu.magisk.data.database.SettingsDao
|
||||||
import com.topjohnwu.magisk.tasks.UpdateRepos
|
import com.topjohnwu.magisk.tasks.UpdateRepos
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.magisk.view.Notifications
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
@ -21,7 +22,7 @@ open class SplashActivity : AppCompatActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
Shell.getShell {
|
Shell.getShell {
|
||||||
if (Config.magiskVersionCode > 0 && Config.magiskVersionCode < Const.MagiskVersion.MIN_SUPPORT) {
|
if (Info.magiskVersionCode > 0 && Info.magiskVersionCode < Const.MagiskVersion.MIN_SUPPORT) {
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.unsupport_magisk_title)
|
.setTitle(R.string.unsupport_magisk_title)
|
||||||
.setMessage(R.string.unsupport_magisk_message)
|
.setMessage(R.string.unsupport_magisk_message)
|
||||||
@ -35,9 +36,9 @@ open class SplashActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initAndStart() {
|
private fun initAndStart() {
|
||||||
val pkg = Config.get<String>(Config.Key.SU_MANAGER)
|
val pkg = Config.suManager
|
||||||
if (pkg != null && packageName == BuildConfig.APPLICATION_ID) {
|
if (Config.suManager.isNotEmpty() && packageName == BuildConfig.APPLICATION_ID) {
|
||||||
Config.remove(Config.Key.SU_MANAGER)
|
get<SettingsDao>().delete(Config.Key.SU_MANAGER)
|
||||||
Shell.su("pm uninstall $pkg").submit()
|
Shell.su("pm uninstall $pkg").submit()
|
||||||
}
|
}
|
||||||
if (TextUtils.equals(pkg, packageName)) {
|
if (TextUtils.equals(pkg, packageName)) {
|
||||||
@ -56,15 +57,12 @@ open class SplashActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Schedule periodic update checks
|
// Schedule periodic update checks
|
||||||
Utils.scheduleUpdateCheck()
|
Utils.scheduleUpdateCheck()
|
||||||
//CheckUpdates.check()
|
|
||||||
|
|
||||||
// Setup shortcuts
|
// Setup shortcuts
|
||||||
Shortcuts.setup(this)
|
Shortcuts.setup(this)
|
||||||
|
|
||||||
// Magisk working as expected
|
// Magisk working as expected
|
||||||
if (Shell.rootAccess() && Config.magiskVersionCode > 0) {
|
if (Shell.rootAccess() && Info.magiskVersionCode > 0) {
|
||||||
// Load modules
|
|
||||||
//Utils.loadModules(false)
|
|
||||||
// Load repos
|
// Load repos
|
||||||
if (Networking.checkNetworkStatus(this)) {
|
if (Networking.checkNetworkStatus(this)) {
|
||||||
get<UpdateRepos>().exec().subscribeK()
|
get<UpdateRepos>().exec().subscribeK()
|
||||||
|
@ -12,8 +12,6 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.preference.*
|
import androidx.preference.*
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.topjohnwu.magisk.App
|
import com.topjohnwu.magisk.App
|
||||||
import com.topjohnwu.magisk.Config
|
|
||||||
import com.topjohnwu.magisk.KConfig
|
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
@ -63,22 +61,6 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat(),
|
|||||||
view.setPadding(0, view.paddingTop, view.paddingRight, view.paddingBottom)
|
view.setPadding(0, view.paddingTop, view.paddingRight, view.paddingBottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun setCustomUpdateChannel(userRepo: String) {
|
|
||||||
KConfig.customUpdateChannel = userRepo
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun getChannelCompat(channel: Int): KConfig.UpdateChannel {
|
|
||||||
return when (channel) {
|
|
||||||
Config.Value.STABLE_CHANNEL,
|
|
||||||
Config.Value.DEFAULT_CHANNEL -> KConfig.UpdateChannel.STABLE
|
|
||||||
Config.Value.BETA_CHANNEL -> KConfig.UpdateChannel.BETA
|
|
||||||
Config.Value.CANARY_CHANNEL -> KConfig.UpdateChannel.CANARY
|
|
||||||
Config.Value.CANARY_DEBUG_CHANNEL -> KConfig.UpdateChannel.CANARY_DEBUG
|
|
||||||
Config.Value.CUSTOM_CHANNEL -> KConfig.UpdateChannel.CUSTOM
|
|
||||||
else -> KConfig.updateChannel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun <T: Preference> findPref(key: CharSequence): T {
|
protected fun <T: Preference> findPref(key: CharSequence): T {
|
||||||
return findPreference(key) as T
|
return findPreference(key) as T
|
||||||
}
|
}
|
||||||
|
@ -51,14 +51,12 @@ abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBin
|
|||||||
get() = navigationController?.let { it.currentStackIndex != defaultPosition } ?: false
|
get() = navigationController?.let { it.currentStackIndex != defaultPosition } ?: false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val isDarkTheme = Config.get<Boolean>(Config.Key.DARK_THEME)
|
val theme = if (Config.darkTheme) {
|
||||||
val theme = if (isDarkTheme) {
|
|
||||||
AppCompatDelegate.MODE_NIGHT_YES
|
AppCompatDelegate.MODE_NIGHT_YES
|
||||||
} else {
|
} else {
|
||||||
AppCompatDelegate.MODE_NIGHT_NO
|
AppCompatDelegate.MODE_NIGHT_NO
|
||||||
}
|
}
|
||||||
AppCompatDelegate.setDefaultNightMode(theme)
|
AppCompatDelegate.setDefaultNightMode(theme)
|
||||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyOverrideConfiguration(config: Configuration?) {
|
override fun applyOverrideConfiguration(config: Configuration?) {
|
||||||
|
@ -28,7 +28,7 @@ class MagiskHideFragment : MagiskFragment<HideViewModel, FragmentMagiskHideBindi
|
|||||||
(findItem(R.id.app_search).actionView as? SearchView)
|
(findItem(R.id.app_search).actionView as? SearchView)
|
||||||
?.setOnQueryTextListener(this@MagiskHideFragment)
|
?.setOnQueryTextListener(this@MagiskHideFragment)
|
||||||
|
|
||||||
val showSystem = Config.get<Boolean>(Config.Key.SHOW_SYSTEM_APP)
|
val showSystem = Config.showSystemApp
|
||||||
|
|
||||||
findItem(R.id.show_system).isChecked = showSystem
|
findItem(R.id.show_system).isChecked = showSystem
|
||||||
viewModel.isShowSystem.value = showSystem
|
viewModel.isShowSystem.value = showSystem
|
||||||
@ -39,10 +39,8 @@ class MagiskHideFragment : MagiskFragment<HideViewModel, FragmentMagiskHideBindi
|
|||||||
if (item.itemId == R.id.show_system) {
|
if (item.itemId == R.id.show_system) {
|
||||||
val showSystem = !item.isChecked
|
val showSystem = !item.isChecked
|
||||||
item.isChecked = showSystem
|
item.isChecked = showSystem
|
||||||
Config.set(Config.Key.SHOW_SYSTEM_APP, showSystem)
|
Config.showSystemApp = showSystem
|
||||||
viewModel.isShowSystem.value = showSystem
|
viewModel.isShowSystem.value = showSystem
|
||||||
//adapter!!.setShowSystem(showSystem)
|
|
||||||
//adapter!!.filter(search!!.query.toString())
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -56,9 +54,4 @@ class MagiskHideFragment : MagiskFragment<HideViewModel, FragmentMagiskHideBindi
|
|||||||
viewModel.query.value = query.orEmpty()
|
viewModel.query.value = query.orEmpty()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/*override fun onEvent(event: Int) {
|
|
||||||
//mSwipeRefreshLayout!!.isRefreshing = false
|
|
||||||
adapter!!.filter(search!!.query.toString())
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ class HomeFragment : MagiskFragment<HomeViewModel, FragmentMagiskBinding>(),
|
|||||||
|
|
||||||
private fun installMagisk() {
|
private fun installMagisk() {
|
||||||
// Show Manager update first
|
// Show Manager update first
|
||||||
if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
if (Info.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
||||||
installManager()
|
installManager()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,7 @@ import com.skoumal.teanity.extensions.addOnPropertyChangedCallback
|
|||||||
import com.skoumal.teanity.extensions.doOnSubscribeUi
|
import com.skoumal.teanity.extensions.doOnSubscribeUi
|
||||||
import com.skoumal.teanity.extensions.subscribeK
|
import com.skoumal.teanity.extensions.subscribeK
|
||||||
import com.skoumal.teanity.util.KObservableField
|
import com.skoumal.teanity.util.KObservableField
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.*
|
||||||
import com.topjohnwu.magisk.Config
|
|
||||||
import com.topjohnwu.magisk.Const
|
|
||||||
import com.topjohnwu.magisk.R
|
|
||||||
import com.topjohnwu.magisk.data.repository.MagiskRepository
|
import com.topjohnwu.magisk.data.repository.MagiskRepository
|
||||||
import com.topjohnwu.magisk.model.events.*
|
import com.topjohnwu.magisk.model.events.*
|
||||||
import com.topjohnwu.magisk.model.observer.Observer
|
import com.topjohnwu.magisk.model.observer.Observer
|
||||||
@ -25,8 +22,8 @@ class HomeViewModel(
|
|||||||
|
|
||||||
val isAdvancedExpanded = KObservableField(false)
|
val isAdvancedExpanded = KObservableField(false)
|
||||||
|
|
||||||
val isForceEncryption = KObservableField(Config.keepEnc)
|
val isForceEncryption = KObservableField(Info.keepEnc)
|
||||||
val isKeepVerity = KObservableField(Config.keepVerity)
|
val isKeepVerity = KObservableField(Info.keepVerity)
|
||||||
|
|
||||||
val magiskState = KObservableField(MagiskState.LOADING)
|
val magiskState = KObservableField(MagiskState.LOADING)
|
||||||
val magiskStateText = Observer(magiskState) {
|
val magiskStateText = Observer(magiskState) {
|
||||||
@ -41,7 +38,7 @@ class HomeViewModel(
|
|||||||
val magiskCurrentVersion = KObservableField("")
|
val magiskCurrentVersion = KObservableField("")
|
||||||
val magiskLatestVersion = KObservableField("")
|
val magiskLatestVersion = KObservableField("")
|
||||||
val magiskAdditionalInfo = Observer(magiskState) {
|
val magiskAdditionalInfo = Observer(magiskState) {
|
||||||
if (Config.get<Boolean>(Config.Key.COREONLY))
|
if (Config.coreOnly)
|
||||||
R.string.core_only_enabled.res()
|
R.string.core_only_enabled.res()
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
@ -87,10 +84,10 @@ class HomeViewModel(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
isForceEncryption.addOnPropertyChangedCallback {
|
isForceEncryption.addOnPropertyChangedCallback {
|
||||||
Config.keepEnc = it ?: return@addOnPropertyChangedCallback
|
Info.keepEnc = it ?: return@addOnPropertyChangedCallback
|
||||||
}
|
}
|
||||||
isKeepVerity.addOnPropertyChangedCallback {
|
isKeepVerity.addOnPropertyChangedCallback {
|
||||||
Config.keepVerity = it ?: return@addOnPropertyChangedCallback
|
Info.keepVerity = it ?: return@addOnPropertyChangedCallback
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh()
|
refresh()
|
||||||
@ -154,7 +151,7 @@ class HomeViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
magiskRepo.fetchConfig()
|
magiskRepo.fetchUpdate()
|
||||||
.applyViewModel(this)
|
.applyViewModel(this)
|
||||||
.doOnSubscribeUi {
|
.doOnSubscribeUi {
|
||||||
magiskState.value = MagiskState.LOADING
|
magiskState.value = MagiskState.LOADING
|
||||||
@ -164,14 +161,6 @@ class HomeViewModel(
|
|||||||
safetyNetTitle.value = R.string.safetyNet_check_text
|
safetyNetTitle.value = R.string.safetyNet_check_text
|
||||||
}
|
}
|
||||||
.subscribeK {
|
.subscribeK {
|
||||||
it.app.let {
|
|
||||||
Config.remoteManagerVersionCode = it.versionCode.toIntOrNull() ?: -1
|
|
||||||
Config.remoteManagerVersionString = it.version
|
|
||||||
}
|
|
||||||
it.magisk.let {
|
|
||||||
Config.remoteMagiskVersionCode = it.versionCode.toIntOrNull() ?: -1
|
|
||||||
Config.remoteMagiskVersionString = it.version
|
|
||||||
}
|
|
||||||
updateSelf()
|
updateSelf()
|
||||||
ensureEnv()
|
ensureEnv()
|
||||||
}
|
}
|
||||||
@ -181,22 +170,22 @@ class HomeViewModel(
|
|||||||
|
|
||||||
private fun updateSelf() {
|
private fun updateSelf() {
|
||||||
state = State.LOADED
|
state = State.LOADED
|
||||||
magiskState.value = when (Config.magiskVersionCode) {
|
magiskState.value = when (Info.magiskVersionCode) {
|
||||||
in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED
|
in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED
|
||||||
!in Config.remoteMagiskVersionCode..Int.MAX_VALUE -> MagiskState.OBSOLETE
|
!in Info.remoteMagiskVersionCode..Int.MAX_VALUE -> MagiskState.OBSOLETE
|
||||||
else -> MagiskState.UP_TO_DATE
|
else -> MagiskState.UP_TO_DATE
|
||||||
}
|
}
|
||||||
|
|
||||||
magiskCurrentVersion.value = if (magiskState.value != MagiskState.NOT_INSTALLED) {
|
magiskCurrentVersion.value = if (magiskState.value != MagiskState.NOT_INSTALLED) {
|
||||||
version.format(Config.magiskVersionString, Config.magiskVersionCode)
|
version.format(Info.magiskVersionString, Info.magiskVersionCode)
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
magiskLatestVersion.value = version
|
magiskLatestVersion.value = version
|
||||||
.format(Config.remoteMagiskVersionString, Config.remoteMagiskVersionCode)
|
.format(Info.remoteMagiskVersionString, Info.remoteMagiskVersionCode)
|
||||||
|
|
||||||
managerState.value = when (Config.remoteManagerVersionCode) {
|
managerState.value = when (Info.remoteManagerVersionCode) {
|
||||||
in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED //wrong update channel
|
in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED //wrong update channel
|
||||||
in (BuildConfig.VERSION_CODE + 1)..Int.MAX_VALUE -> MagiskState.OBSOLETE
|
in (BuildConfig.VERSION_CODE + 1)..Int.MAX_VALUE -> MagiskState.OBSOLETE
|
||||||
else -> MagiskState.UP_TO_DATE
|
else -> MagiskState.UP_TO_DATE
|
||||||
@ -206,7 +195,7 @@ class HomeViewModel(
|
|||||||
.format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
|
.format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
|
||||||
|
|
||||||
managerLatestVersion.value = version
|
managerLatestVersion.value = version
|
||||||
.format(Config.remoteManagerVersionString, Config.remoteManagerVersionCode)
|
.format(Info.remoteManagerVersionString, Info.remoteManagerVersionCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ensureEnv() {
|
private fun ensureEnv() {
|
||||||
|
@ -53,9 +53,9 @@ class ReposFragment : MagiskFragment<ModuleViewModel, FragmentReposBinding>(),
|
|||||||
.setTitle(R.string.sorting_order)
|
.setTitle(R.string.sorting_order)
|
||||||
.setSingleChoiceItems(
|
.setSingleChoiceItems(
|
||||||
R.array.sorting_orders,
|
R.array.sorting_orders,
|
||||||
Config.get<Int>(Config.Key.REPO_ORDER)!!
|
Config.repoOrder
|
||||||
) { d, which ->
|
) { d, which ->
|
||||||
Config.set(Config.Key.REPO_ORDER, which)
|
Config.repoOrder = which
|
||||||
viewModel.refresh(false)
|
viewModel.refresh(false)
|
||||||
d.dismiss()
|
d.dismiss()
|
||||||
}.show()
|
}.show()
|
||||||
|
@ -13,10 +13,11 @@ import androidx.preference.Preference
|
|||||||
import androidx.preference.PreferenceCategory
|
import androidx.preference.PreferenceCategory
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import com.skoumal.teanity.extensions.subscribeK
|
import com.skoumal.teanity.extensions.subscribeK
|
||||||
import com.topjohnwu.magisk.*
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
import com.topjohnwu.magisk.KConfig.UpdateChannel
|
import com.topjohnwu.magisk.Config
|
||||||
|
import com.topjohnwu.magisk.Const
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper
|
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper
|
||||||
import com.topjohnwu.magisk.data.repository.SettingRepository
|
|
||||||
import com.topjohnwu.magisk.ui.base.BasePreferenceFragment
|
import com.topjohnwu.magisk.ui.base.BasePreferenceFragment
|
||||||
import com.topjohnwu.magisk.utils.*
|
import com.topjohnwu.magisk.utils.*
|
||||||
import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog
|
import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog
|
||||||
@ -27,7 +28,6 @@ import org.koin.android.ext.android.inject
|
|||||||
class SettingsFragment : BasePreferenceFragment() {
|
class SettingsFragment : BasePreferenceFragment() {
|
||||||
|
|
||||||
private val repoDatabase: RepoDatabaseHelper by inject()
|
private val repoDatabase: RepoDatabaseHelper by inject()
|
||||||
private val settingRepo: SettingRepository by inject()
|
|
||||||
|
|
||||||
private lateinit var updateChannel: ListPreference
|
private lateinit var updateChannel: ListPreference
|
||||||
private lateinit var autoRes: ListPreference
|
private lateinit var autoRes: ListPreference
|
||||||
@ -53,7 +53,7 @@ class SettingsFragment : BasePreferenceFragment() {
|
|||||||
requestTimeout = findPref(Config.Key.SU_REQUEST_TIMEOUT)
|
requestTimeout = findPref(Config.Key.SU_REQUEST_TIMEOUT)
|
||||||
suNotification = findPref(Config.Key.SU_NOTIFICATION)
|
suNotification = findPref(Config.Key.SU_NOTIFICATION)
|
||||||
multiuserConfig = findPref(Config.Key.SU_MULTIUSER_MODE)
|
multiuserConfig = findPref(Config.Key.SU_MULTIUSER_MODE)
|
||||||
nsConfig = findPref(Config.Key.SU_MNT_NS) as ListPreference
|
nsConfig = findPref(Config.Key.SU_MNT_NS)
|
||||||
val reauth = findPreference(Config.Key.SU_REAUTH) as SwitchPreferenceCompat
|
val reauth = findPreference(Config.Key.SU_REAUTH) as SwitchPreferenceCompat
|
||||||
val fingerprint = findPreference(Config.Key.SU_FINGERPRINT) as SwitchPreferenceCompat
|
val fingerprint = findPreference(Config.Key.SU_FINGERPRINT) as SwitchPreferenceCompat
|
||||||
val generalCatagory = findPreference("general") as PreferenceCategory
|
val generalCatagory = findPreference("general") as PreferenceCategory
|
||||||
@ -70,7 +70,9 @@ class SettingsFragment : BasePreferenceFragment() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
findPreference("clear").setOnPreferenceClickListener {
|
findPreference("clear").setOnPreferenceClickListener {
|
||||||
prefs.edit().remove(Config.Key.ETAG_KEY).apply()
|
prefs.edit {
|
||||||
|
remove(Config.Key.ETAG_KEY)
|
||||||
|
}
|
||||||
repoDatabase.clearRepo()
|
repoDatabase.clearRepo()
|
||||||
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT)
|
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT)
|
||||||
true
|
true
|
||||||
@ -81,30 +83,23 @@ class SettingsFragment : BasePreferenceFragment() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs.edit {
|
|
||||||
putBoolean(Config.Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint())
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChannel.setOnPreferenceChangeListener { _, value ->
|
updateChannel.setOnPreferenceChangeListener { _, value ->
|
||||||
val channel = Integer.parseInt(value as String)
|
val channel = Integer.parseInt(value as String)
|
||||||
|
val previous = Config.updateChannel
|
||||||
|
|
||||||
val previousUpdateChannel = KConfig.updateChannel
|
if (channel == Config.Value.CUSTOM_CHANNEL) {
|
||||||
val updateChannel = getChannelCompat(channel)
|
val v = LayoutInflater.from(requireActivity())
|
||||||
|
.inflate(R.layout.custom_channel_dialog, null)
|
||||||
KConfig.updateChannel = updateChannel
|
|
||||||
|
|
||||||
if (updateChannel === UpdateChannel.CUSTOM) {
|
|
||||||
val v = LayoutInflater.from(requireActivity()).inflate(R.layout.custom_channel_dialog, null)
|
|
||||||
val url = v.findViewById<EditText>(R.id.custom_url)
|
val url = v.findViewById<EditText>(R.id.custom_url)
|
||||||
url.setText(KConfig.customUpdateChannel)
|
url.setText(Config.customChannelUrl)
|
||||||
AlertDialog.Builder(requireActivity())
|
AlertDialog.Builder(requireActivity())
|
||||||
.setTitle(R.string.settings_update_custom)
|
.setTitle(R.string.settings_update_custom)
|
||||||
.setView(v)
|
.setView(v)
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
setCustomUpdateChannel(url.text.toString()) }
|
Config.customChannelUrl = url.text.toString() }
|
||||||
.setNegativeButton(R.string.close) { _, _ ->
|
.setNegativeButton(R.string.close) { _, _ ->
|
||||||
KConfig.updateChannel = previousUpdateChannel }
|
Config.updateChannel = previous }
|
||||||
.setOnCancelListener { KConfig.updateChannel = previousUpdateChannel }
|
.setOnCancelListener { Config.updateChannel = previous }
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@ -114,7 +109,7 @@ class SettingsFragment : BasePreferenceFragment() {
|
|||||||
|
|
||||||
/* We only show canary channels if user is already on canary channel
|
/* We only show canary channels if user is already on canary channel
|
||||||
* or the user have already chosen canary channel */
|
* or the user have already chosen canary channel */
|
||||||
if (!Utils.isCanary() && Config.get<Int>(Config.Key.UPDATE_CHANNEL) < Config.Value.CANARY_CHANNEL) {
|
if (!Utils.isCanary && Config.updateChannel < Config.Value.CANARY_CHANNEL) {
|
||||||
// Remove the last 2 entries
|
// Remove the last 2 entries
|
||||||
val entries = updateChannel.entries
|
val entries = updateChannel.entries
|
||||||
updateChannel.entries = entries.copyOf(entries.size - 2)
|
updateChannel.entries = entries.copyOf(entries.size - 2)
|
||||||
@ -168,8 +163,9 @@ class SettingsFragment : BasePreferenceFragment() {
|
|||||||
|
|
||||||
override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String) {
|
override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String) {
|
||||||
when (key) {
|
when (key) {
|
||||||
Config.Key.ROOT_ACCESS, Config.Key.SU_MULTIUSER_MODE, Config.Key.SU_MNT_NS ->
|
Config.Key.ROOT_ACCESS -> Config.rootMode = Utils.getPrefsInt(prefs, key)
|
||||||
settingRepo.put(key, Utils.getPrefsInt(prefs, key)).subscribe()
|
Config.Key.SU_MULTIUSER_MODE -> Config.suMultiuserMode = Utils.getPrefsInt(prefs, key)
|
||||||
|
Config.Key.SU_MNT_NS -> Config.suMntNamespaceMode = Utils.getPrefsInt(prefs, key)
|
||||||
Config.Key.DARK_THEME -> requireActivity().recreate()
|
Config.Key.DARK_THEME -> requireActivity().recreate()
|
||||||
Config.Key.COREONLY -> {
|
Config.Key.COREONLY -> {
|
||||||
if (prefs.getBoolean(key, false)) {
|
if (prefs.getBoolean(key, false)) {
|
||||||
@ -196,14 +192,13 @@ class SettingsFragment : BasePreferenceFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPreferenceTreeClick(preference: Preference): Boolean {
|
override fun onPreferenceTreeClick(preference: Preference): Boolean {
|
||||||
val key = preference.key
|
when (preference.key) {
|
||||||
when (key) {
|
|
||||||
Config.Key.SU_FINGERPRINT -> {
|
Config.Key.SU_FINGERPRINT -> {
|
||||||
val checked = (preference as SwitchPreferenceCompat).isChecked
|
val checked = (preference as SwitchPreferenceCompat).isChecked
|
||||||
preference.isChecked = !checked
|
preference.isChecked = !checked
|
||||||
FingerprintAuthDialog(requireActivity()) {
|
FingerprintAuthDialog(requireActivity()) {
|
||||||
preference.isChecked = checked
|
preference.isChecked = checked
|
||||||
Config.set(key, checked)
|
Config.suFingerprint = checked
|
||||||
}.show()
|
}.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,34 +232,34 @@ class SettingsFragment : BasePreferenceFragment() {
|
|||||||
|
|
||||||
private fun setSummary(key: String) {
|
private fun setSummary(key: String) {
|
||||||
when (key) {
|
when (key) {
|
||||||
|
Config.Key.ROOT_ACCESS -> rootConfig.summary = resources
|
||||||
|
.getStringArray(R.array.su_access)[Config.rootMode]
|
||||||
|
Config.Key.SU_MULTIUSER_MODE -> multiuserConfig.summary = resources
|
||||||
|
.getStringArray(R.array.multiuser_summary)[Config.suMultiuserMode]
|
||||||
|
Config.Key.SU_MNT_NS -> nsConfig.summary = resources
|
||||||
|
.getStringArray(R.array.namespace_summary)[Config.suMntNamespaceMode]
|
||||||
Config.Key.UPDATE_CHANNEL -> {
|
Config.Key.UPDATE_CHANNEL -> {
|
||||||
var ch = Config.get<Int>(key)
|
var ch = Config.updateChannel
|
||||||
ch = if (ch < 0) Config.Value.STABLE_CHANNEL else ch
|
ch = if (ch < 0) Config.Value.STABLE_CHANNEL else ch
|
||||||
updateChannel.summary = resources
|
updateChannel.summary = resources
|
||||||
.getStringArray(R.array.update_channel)[ch]
|
.getStringArray(R.array.update_channel)[ch]
|
||||||
}
|
}
|
||||||
Config.Key.ROOT_ACCESS -> rootConfig.summary = resources
|
|
||||||
.getStringArray(R.array.su_access)[Config.get<Int>(key)]
|
|
||||||
Config.Key.SU_AUTO_RESPONSE -> autoRes.summary = resources
|
Config.Key.SU_AUTO_RESPONSE -> autoRes.summary = resources
|
||||||
.getStringArray(R.array.auto_response)[Config.get<Int>(key)]
|
.getStringArray(R.array.auto_response)[Config.suAutoReponse]
|
||||||
Config.Key.SU_NOTIFICATION -> suNotification.summary = resources
|
Config.Key.SU_NOTIFICATION -> suNotification.summary = resources
|
||||||
.getStringArray(R.array.su_notification)[Config.get<Int>(key)]
|
.getStringArray(R.array.su_notification)[Config.suNotification]
|
||||||
Config.Key.SU_REQUEST_TIMEOUT -> requestTimeout.summary =
|
Config.Key.SU_REQUEST_TIMEOUT -> requestTimeout.summary =
|
||||||
getString(R.string.request_timeout_summary, Config.get<Int>(key))
|
getString(R.string.request_timeout_summary, Config.suDefaultTimeout)
|
||||||
Config.Key.SU_MULTIUSER_MODE -> multiuserConfig.summary =
|
|
||||||
resources.getStringArray(R.array.multiuser_summary)[Config.get<Int>(key)]
|
|
||||||
Config.Key.SU_MNT_NS -> nsConfig.summary = resources
|
|
||||||
.getStringArray(R.array.namespace_summary)[Config.get<Int>(key)]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSummary() {
|
private fun setSummary() {
|
||||||
setSummary(Config.Key.UPDATE_CHANNEL)
|
|
||||||
setSummary(Config.Key.ROOT_ACCESS)
|
setSummary(Config.Key.ROOT_ACCESS)
|
||||||
|
setSummary(Config.Key.SU_MULTIUSER_MODE)
|
||||||
|
setSummary(Config.Key.SU_MNT_NS)
|
||||||
|
setSummary(Config.Key.UPDATE_CHANNEL)
|
||||||
setSummary(Config.Key.SU_AUTO_RESPONSE)
|
setSummary(Config.Key.SU_AUTO_RESPONSE)
|
||||||
setSummary(Config.Key.SU_NOTIFICATION)
|
setSummary(Config.Key.SU_NOTIFICATION)
|
||||||
setSummary(Config.Key.SU_REQUEST_TIMEOUT)
|
setSummary(Config.Key.SU_REQUEST_TIMEOUT)
|
||||||
setSummary(Config.Key.SU_MULTIUSER_MODE)
|
|
||||||
setSummary(Config.Key.SU_MNT_NS)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ class SuRequestViewModel(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
when (Config.get<Int>(Config.Key.SU_AUTO_RESPONSE)) {
|
when (Config.suAutoReponse) {
|
||||||
Config.Value.SU_AUTO_DENY -> {
|
Config.Value.SU_AUTO_DENY -> {
|
||||||
handler?.handleAction(Policy.DENY, 0)
|
handler?.handleAction(Policy.DENY, 0)
|
||||||
return true
|
return true
|
||||||
@ -190,8 +190,7 @@ class SuRequestViewModel(
|
|||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private fun showUI() {
|
private fun showUI() {
|
||||||
val seconds = Config.get<Int>(Config.Key.SU_REQUEST_TIMEOUT).toLong()
|
val millis = SECONDS.toMillis(Config.suDefaultTimeout.toLong())
|
||||||
val millis = SECONDS.toMillis(seconds)
|
|
||||||
timer = object : CountDownTimer(millis, 1000) {
|
timer = object : CountDownTimer(millis, 1000) {
|
||||||
override fun onTick(remains: Long) {
|
override fun onTick(remains: Long) {
|
||||||
denyText.value = "%s (%d)"
|
denyText.value = "%s (%d)"
|
||||||
|
@ -6,6 +6,7 @@ import com.topjohnwu.magisk.App;
|
|||||||
import com.topjohnwu.magisk.BuildConfig;
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
import com.topjohnwu.magisk.ClassMap;
|
||||||
import com.topjohnwu.magisk.Config;
|
import com.topjohnwu.magisk.Config;
|
||||||
|
import com.topjohnwu.magisk.Info;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.ui.SplashActivity;
|
import com.topjohnwu.magisk.ui.SplashActivity;
|
||||||
import com.topjohnwu.magisk.view.ProgressNotification;
|
import com.topjohnwu.magisk.view.ProgressNotification;
|
||||||
@ -24,8 +25,8 @@ public class DownloadApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void restore() {
|
public static void restore() {
|
||||||
String name = Utils.fmt("MagiskManager v%s(%d)",
|
String name = Utils.INSTANCE.fmt("MagiskManager v%s(%d)",
|
||||||
Config.remoteManagerVersionString, Config.remoteManagerVersionCode);
|
Info.remoteManagerVersionString, Info.remoteManagerVersionCode);
|
||||||
dlInstall(name, new RestoreManager());
|
dlInstall(name, new RestoreManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ public class DownloadApp {
|
|||||||
File apk = new File(App.self.getCacheDir(), "manager.apk");
|
File apk = new File(App.self.getCacheDir(), "manager.apk");
|
||||||
ProgressNotification progress = new ProgressNotification(name);
|
ProgressNotification progress = new ProgressNotification(name);
|
||||||
listener.progress = progress;
|
listener.progress = progress;
|
||||||
Networking.get(Config.managerLink)
|
Networking.get(Info.managerLink)
|
||||||
.setExecutor(App.THREAD_POOL)
|
.setExecutor(App.THREAD_POOL)
|
||||||
.setDownloadProgressListener(progress)
|
.setDownloadProgressListener(progress)
|
||||||
.setErrorHandler((conn, e) -> progress.dlFail())
|
.setErrorHandler((conn, e) -> progress.dlFail())
|
||||||
|
@ -1,124 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.KeyguardManager;
|
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.CancellationSignal;
|
|
||||||
import android.security.keystore.KeyGenParameterSpec;
|
|
||||||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
|
||||||
import android.security.keystore.KeyProperties;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
|
||||||
import com.topjohnwu.magisk.Config;
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
|
|
||||||
import java.security.KeyStore;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.KeyGenerator;
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
|
||||||
public abstract class FingerprintHelper {
|
|
||||||
|
|
||||||
private FingerprintManager manager;
|
|
||||||
private Cipher cipher;
|
|
||||||
private CancellationSignal cancel;
|
|
||||||
private static final String SU_KEYSTORE_KEY = "su_key";
|
|
||||||
|
|
||||||
public static boolean useFingerprint() {
|
|
||||||
boolean fp = Config.get(Config.Key.SU_FINGERPRINT);
|
|
||||||
if (fp && !canUseFingerprint()) {
|
|
||||||
Config.set(Config.Key.SU_FINGERPRINT, false);
|
|
||||||
fp = false;
|
|
||||||
}
|
|
||||||
return fp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean canUseFingerprint() {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
|
||||||
return false;
|
|
||||||
KeyguardManager km = App.self.getSystemService(KeyguardManager.class);
|
|
||||||
FingerprintManager fm = App.self.getSystemService(FingerprintManager.class);
|
|
||||||
return km.isKeyguardSecure() && fm != null && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected FingerprintHelper() throws Exception {
|
|
||||||
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
|
||||||
manager = App.self.getSystemService(FingerprintManager.class);
|
|
||||||
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
|
|
||||||
+ KeyProperties.BLOCK_MODE_CBC + "/"
|
|
||||||
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
|
||||||
keyStore.load(null);
|
|
||||||
SecretKey key = (SecretKey) keyStore.getKey(SU_KEYSTORE_KEY, null);
|
|
||||||
if (key == null) {
|
|
||||||
key = generateKey();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
|
||||||
} catch (KeyPermanentlyInvalidatedException e) {
|
|
||||||
// Only happens on Marshmallow
|
|
||||||
key = generateKey();
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void onAuthenticationError(int errorCode, CharSequence errString);
|
|
||||||
|
|
||||||
public abstract void onAuthenticationHelp(int helpCode, CharSequence helpString);
|
|
||||||
|
|
||||||
public abstract void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result);
|
|
||||||
|
|
||||||
public abstract void onAuthenticationFailed();
|
|
||||||
|
|
||||||
public void authenticate() {
|
|
||||||
cancel = new CancellationSignal();
|
|
||||||
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
|
|
||||||
manager.authenticate(cryptoObject, cancel, 0, new Callback(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel() {
|
|
||||||
if (cancel != null)
|
|
||||||
cancel.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private SecretKey generateKey() throws Exception {
|
|
||||||
KeyGenerator keygen = KeyGenerator
|
|
||||||
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
|
|
||||||
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
|
|
||||||
SU_KEYSTORE_KEY,
|
|
||||||
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
|
|
||||||
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
|
||||||
.setUserAuthenticationRequired(true)
|
|
||||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
builder.setInvalidatedByBiometricEnrollment(false);
|
|
||||||
}
|
|
||||||
keygen.init(builder.build());
|
|
||||||
return keygen.generateKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Callback extends FingerprintManager.AuthenticationCallback {
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
|
||||||
FingerprintHelper.this.onAuthenticationError(errorCode, errString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
|
||||||
FingerprintHelper.this.onAuthenticationHelp(helpCode, helpString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
|
||||||
FingerprintHelper.this.onAuthenticationSucceeded(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationFailed() {
|
|
||||||
FingerprintHelper.this.onAuthenticationFailed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,119 @@
|
|||||||
|
package com.topjohnwu.magisk.utils
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.app.KeyguardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.hardware.fingerprint.FingerprintManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.CancellationSignal
|
||||||
|
import android.security.keystore.KeyGenParameterSpec
|
||||||
|
import android.security.keystore.KeyProperties
|
||||||
|
import com.topjohnwu.magisk.Config
|
||||||
|
import java.security.KeyStore
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.KeyGenerator
|
||||||
|
import javax.crypto.SecretKey
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
|
abstract class FingerprintHelper @Throws(Exception::class)
|
||||||
|
protected constructor() {
|
||||||
|
|
||||||
|
private val manager: FingerprintManager?
|
||||||
|
private val cipher: Cipher
|
||||||
|
private var cancel: CancellationSignal? = null
|
||||||
|
private val context: Context by inject()
|
||||||
|
|
||||||
|
init {
|
||||||
|
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
||||||
|
manager = context.getSystemService(FingerprintManager::class.java)
|
||||||
|
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
|
||||||
|
+ KeyProperties.BLOCK_MODE_CBC + "/"
|
||||||
|
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
|
||||||
|
keyStore.load(null)
|
||||||
|
var key = keyStore.getKey(SU_KEYSTORE_KEY, null) as SecretKey? ?: generateKey()
|
||||||
|
runCatching {
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key)
|
||||||
|
}.onFailure {
|
||||||
|
// Only happens on Marshmallow
|
||||||
|
key = generateKey()
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun onAuthenticationError(errorCode: Int, errString: CharSequence)
|
||||||
|
|
||||||
|
abstract fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence)
|
||||||
|
|
||||||
|
abstract fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult)
|
||||||
|
|
||||||
|
abstract fun onAuthenticationFailed()
|
||||||
|
|
||||||
|
fun authenticate() {
|
||||||
|
cancel = CancellationSignal()
|
||||||
|
val cryptoObject = FingerprintManager.CryptoObject(cipher)
|
||||||
|
manager!!.authenticate(cryptoObject, cancel, 0, Callback(), null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
if (cancel != null)
|
||||||
|
cancel!!.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
private fun generateKey(): SecretKey {
|
||||||
|
val keygen = KeyGenerator
|
||||||
|
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
|
||||||
|
val builder = KeyGenParameterSpec.Builder(
|
||||||
|
SU_KEYSTORE_KEY,
|
||||||
|
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
||||||
|
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
||||||
|
.setUserAuthenticationRequired(true)
|
||||||
|
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
builder.setInvalidatedByBiometricEnrollment(false)
|
||||||
|
}
|
||||||
|
keygen.init(builder.build())
|
||||||
|
return keygen.generateKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class Callback : FingerprintManager.AuthenticationCallback() {
|
||||||
|
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||||
|
this@FingerprintHelper.onAuthenticationError(errorCode, errString)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) {
|
||||||
|
this@FingerprintHelper.onAuthenticationHelp(helpCode, helpString)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
|
||||||
|
this@FingerprintHelper.onAuthenticationSucceeded(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationFailed() {
|
||||||
|
this@FingerprintHelper.onAuthenticationFailed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SU_KEYSTORE_KEY = "su_key"
|
||||||
|
|
||||||
|
fun useFingerprint(): Boolean {
|
||||||
|
var fp = Config.suFingerprint
|
||||||
|
if (fp && !canUseFingerprint()) {
|
||||||
|
Config.suFingerprint = false
|
||||||
|
fp = false
|
||||||
|
}
|
||||||
|
return fp
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canUseFingerprint(context: Context = get()): Boolean {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||||
|
return false
|
||||||
|
val km = context.getSystemService(KeyguardManager::class.java)
|
||||||
|
val fm = context.getSystemService(FingerprintManager::class.java)
|
||||||
|
return km?.isKeyguardSecure ?: false &&
|
||||||
|
fm != null && fm.isHardwareDetected && fm.hasEnrolledFingerprints()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -107,9 +107,9 @@ object LocaleManager {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setLocale(wrapper: ContextWrapper) {
|
fun setLocale(wrapper: ContextWrapper) {
|
||||||
val localeConfig = Config.get<String>(Config.Key.LOCALE)
|
val localeConfig = Config.locale
|
||||||
locale = when {
|
locale = when {
|
||||||
localeConfig.isNullOrEmpty() -> defaultLocale
|
localeConfig.isEmpty() -> defaultLocale
|
||||||
else -> forLanguageTag(localeConfig)
|
else -> forLanguageTag(localeConfig)
|
||||||
}
|
}
|
||||||
Locale.setDefault(locale)
|
Locale.setDefault(locale)
|
||||||
|
@ -13,7 +13,7 @@ public class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void debug(String fmt, Object... args) {
|
public static void debug(String fmt, Object... args) {
|
||||||
debug(Utils.fmt(fmt, args));
|
debug(Utils.INSTANCE.fmt(fmt, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void error(String line) {
|
public static void error(String line) {
|
||||||
@ -21,6 +21,6 @@ public class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void error(String fmt, Object... args) {
|
public static void error(String fmt, Object... args) {
|
||||||
error(Utils.fmt(fmt, args));
|
error(Utils.INSTANCE.fmt(fmt, args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package com.topjohnwu.magisk.utils;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
import com.topjohnwu.magisk.App;
|
||||||
import com.topjohnwu.magisk.BuildConfig;
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
import com.topjohnwu.magisk.ClassMap;
|
||||||
@ -27,8 +29,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
|
|
||||||
public class PatchAPK {
|
public class PatchAPK {
|
||||||
|
|
||||||
public static final String LOWERALPHA = "abcdefghijklmnopqrstuvwxyz";
|
public static final String LOWERALPHA = "abcdefghijklmnopqrstuvwxyz";
|
||||||
@ -110,7 +110,7 @@ public class PatchAPK {
|
|||||||
if (!Shell.su("pm install " + repack).exec().isSuccess())
|
if (!Shell.su("pm install " + repack).exec().isSuccess())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Config.set(Config.Key.SU_MANAGER, pkg);
|
Config.setSuManager(pkg);
|
||||||
Config.export();
|
Config.export();
|
||||||
RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID,
|
RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID,
|
||||||
new ComponentName(pkg, ClassMap.get(SplashActivity.class).getName()));
|
new ComponentName(pkg, ClassMap.get(SplashActivity.class).getName()));
|
||||||
@ -145,7 +145,7 @@ public class PatchAPK {
|
|||||||
Notifications.progress(app.getString(R.string.hide_manager_title));
|
Notifications.progress(app.getString(R.string.hide_manager_title));
|
||||||
Notifications.mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build());
|
Notifications.mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build());
|
||||||
if(!patchAndHide())
|
if(!patchAndHide())
|
||||||
Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
|
Utils.INSTANCE.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
|
||||||
Notifications.mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID);
|
Notifications.mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ import android.content.ComponentName
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.topjohnwu.magisk.Config
|
|
||||||
import com.topjohnwu.magisk.Const
|
import com.topjohnwu.magisk.Const
|
||||||
|
import com.topjohnwu.magisk.Info
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.ShellUtils
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
@ -132,7 +132,7 @@ class RootUtils : Shell.Initializer() {
|
|||||||
job.add(context.rawResource(R.raw.util_functions))
|
job.add(context.rawResource(R.raw.util_functions))
|
||||||
.add(context.rawResource(R.raw.utils))
|
.add(context.rawResource(R.raw.utils))
|
||||||
Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk")
|
Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk")
|
||||||
Config.loadMagiskInfo()
|
Info.loadMagiskInfo()
|
||||||
} else {
|
} else {
|
||||||
job.add(context.rawResource(R.raw.nonroot_utils))
|
job.add(context.rawResource(R.raw.nonroot_utils))
|
||||||
}
|
}
|
||||||
@ -143,9 +143,9 @@ class RootUtils : Shell.Initializer() {
|
|||||||
"export BOOTMODE=true")
|
"export BOOTMODE=true")
|
||||||
.exec()
|
.exec()
|
||||||
|
|
||||||
Config.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean()
|
Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean()
|
||||||
Config.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean()
|
Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean()
|
||||||
Config.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean()
|
Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ class RootUtils : Shell.Initializer() {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun reboot() {
|
fun reboot() {
|
||||||
Shell.su("/system/bin/reboot ${if (Config.recovery) "recovery" else ""}").submit()
|
Shell.su("/system/bin/reboot ${if (Info.recovery) "recovery" else ""}").submit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package com.topjohnwu.magisk.utils
|
package com.topjohnwu.magisk.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.topjohnwu.magisk.App
|
|
||||||
import com.topjohnwu.magisk.Config
|
import com.topjohnwu.magisk.Config
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.data.repository.AppRepository
|
import com.topjohnwu.magisk.data.repository.AppRepository
|
||||||
@ -17,6 +17,8 @@ import java.util.*
|
|||||||
|
|
||||||
object SuLogger {
|
object SuLogger {
|
||||||
|
|
||||||
|
private val context: Context by inject()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun handleLogs(intent: Intent) {
|
fun handleLogs(intent: Intent) {
|
||||||
|
|
||||||
@ -66,9 +68,9 @@ object SuLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleNotify(policy: MagiskPolicy) {
|
private fun handleNotify(policy: MagiskPolicy) {
|
||||||
if (policy.notification && Config.get<Any>(Config.Key.SU_NOTIFICATION) as Int == Config.Value.NOTIFICATION_TOAST) {
|
if (policy.notification && Config.suNotification == Config.Value.NOTIFICATION_TOAST) {
|
||||||
Utils.toast(
|
Utils.toast(
|
||||||
App.self.getString(
|
context.getString(
|
||||||
if (policy.policy == Policy.ALLOW)
|
if (policy.policy == Policy.ALLOW)
|
||||||
R.string.su_allow_toast
|
R.string.su_allow_toast
|
||||||
else
|
else
|
||||||
|
@ -1,142 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
|
||||||
import com.topjohnwu.magisk.BuildConfig;
|
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
|
||||||
import com.topjohnwu.magisk.Config;
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.model.entity.OldModule;
|
|
||||||
import com.topjohnwu.magisk.model.update.UpdateCheckService;
|
|
||||||
import com.topjohnwu.net.Networking;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import androidx.annotation.WorkerThread;
|
|
||||||
import androidx.work.Constraints;
|
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
|
||||||
import androidx.work.ListenableWorker;
|
|
||||||
import androidx.work.NetworkType;
|
|
||||||
import androidx.work.PeriodicWorkRequest;
|
|
||||||
import androidx.work.WorkManager;
|
|
||||||
|
|
||||||
public class Utils {
|
|
||||||
|
|
||||||
public static void toast(CharSequence msg, int duration) {
|
|
||||||
UiThreadHandler.run(() -> Toast.makeText(App.self, msg, duration).show());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void toast(int resId, int duration) {
|
|
||||||
UiThreadHandler.run(() -> Toast.makeText(App.self, resId, duration).show());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String dlString(String url) {
|
|
||||||
String s = Networking.get(url).execForString().getResult();
|
|
||||||
return s == null ? "" : s;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getPrefsInt(SharedPreferences prefs, String key, int def) {
|
|
||||||
return Integer.parseInt(prefs.getString(key, String.valueOf(def)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getPrefsInt(SharedPreferences prefs, String key) {
|
|
||||||
return getPrefsInt(prefs, key, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int dpInPx(int dp) {
|
|
||||||
float scale = App.self.getResources().getDisplayMetrics().density;
|
|
||||||
return (int) (dp * scale + 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String fmt(String fmt, Object... args) {
|
|
||||||
return String.format(Locale.US, fmt, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
|
|
||||||
try {
|
|
||||||
if (info.labelRes > 0) {
|
|
||||||
Resources res = pm.getResourcesForApplication(info);
|
|
||||||
Configuration config = new Configuration();
|
|
||||||
config.setLocale(LocaleManager.getLocale());
|
|
||||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
|
||||||
return res.getString(info.labelRes);
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
return info.loadLabel(pm).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getLegalFilename(CharSequence filename) {
|
|
||||||
return filename.toString().replace(" ", "_").replace("'", "").replace("\"", "")
|
|
||||||
.replace("$", "").replace("`", "").replace("*", "").replace("/", "_")
|
|
||||||
.replace("#", "").replace("@", "").replace("\\", "_");
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
public static Map<String, OldModule> loadModulesLeanback() {
|
|
||||||
final Map<String, OldModule> moduleMap = new ValueSortedMap<>();
|
|
||||||
final SuFile path = new SuFile(Const.MAGISK_PATH);
|
|
||||||
final SuFile[] modules = path.listFiles((file, name) ->
|
|
||||||
!name.equals("lost+found") && !name.equals(".core")
|
|
||||||
);
|
|
||||||
for (SuFile file : modules) {
|
|
||||||
if (file.isFile()) continue;
|
|
||||||
OldModule module = new OldModule(Const.MAGISK_PATH + "/" + file.getName());
|
|
||||||
moduleMap.put(module.getId(), module);
|
|
||||||
}
|
|
||||||
return moduleMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean showSuperUser() {
|
|
||||||
return Shell.rootAccess() && (Const.USER_ID == 0 ||
|
|
||||||
(int) Config.get(Config.Key.SU_MULTIUSER_MODE) !=
|
|
||||||
Config.Value.MULTIUSER_MODE_OWNER_MANAGED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isCanary() {
|
|
||||||
return BuildConfig.VERSION_NAME.contains("-");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void scheduleUpdateCheck() {
|
|
||||||
if (Config.get(Config.Key.CHECK_UPDATES)) {
|
|
||||||
Constraints constraints = new Constraints.Builder()
|
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
|
||||||
.setRequiresDeviceIdle(true)
|
|
||||||
.build();
|
|
||||||
PeriodicWorkRequest request = new PeriodicWorkRequest
|
|
||||||
.Builder(ClassMap.get(UpdateCheckService.class), 12, TimeUnit.HOURS)
|
|
||||||
.setConstraints(constraints)
|
|
||||||
.build();
|
|
||||||
WorkManager.getInstance().enqueueUniquePeriodicWork(
|
|
||||||
Const.ID.CHECK_MAGISK_UPDATE_WORKER_ID,
|
|
||||||
ExistingPeriodicWorkPolicy.REPLACE, request);
|
|
||||||
} else {
|
|
||||||
WorkManager.getInstance().cancelUniqueWork(Const.ID.CHECK_MAGISK_UPDATE_WORKER_ID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void openLink(Context context, Uri link) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, link);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
if (intent.resolveActivity(context.getPackageManager()) != null) {
|
|
||||||
context.startActivity(intent);
|
|
||||||
} else {
|
|
||||||
toast(R.string.open_link_failed_toast, Toast.LENGTH_SHORT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
123
app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt
Normal file
123
app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package com.topjohnwu.magisk.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.content.pm.ApplicationInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.net.Uri
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
|
import androidx.work.*
|
||||||
|
import com.topjohnwu.magisk.*
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.model.entity.OldModule
|
||||||
|
import com.topjohnwu.magisk.model.update.UpdateCheckService
|
||||||
|
import com.topjohnwu.net.Networking
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object Utils {
|
||||||
|
|
||||||
|
val isCanary: Boolean
|
||||||
|
get() = BuildConfig.VERSION_NAME.contains("-")
|
||||||
|
|
||||||
|
fun toast(msg: CharSequence, duration: Int) {
|
||||||
|
UiThreadHandler.run { Toast.makeText(get(), msg, duration).show() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toast(resId: Int, duration: Int) {
|
||||||
|
UiThreadHandler.run { Toast.makeText(get(), resId, duration).show() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dlString(url: String): String {
|
||||||
|
val s = Networking.get(url).execForString().result
|
||||||
|
return s ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPrefsInt(prefs: SharedPreferences, key: String, def: Int = 0): Int {
|
||||||
|
return prefs.getString(key, def.toString())!!.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dpInPx(dp: Int): Int {
|
||||||
|
val scale = get<Resources>().displayMetrics.density
|
||||||
|
return (dp * scale + 0.5).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fmt(fmt: String, vararg args: Any): String {
|
||||||
|
return String.format(Locale.US, fmt, *args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAppLabel(info: ApplicationInfo, pm: PackageManager): String {
|
||||||
|
try {
|
||||||
|
if (info.labelRes > 0) {
|
||||||
|
val res = pm.getResourcesForApplication(info)
|
||||||
|
val config = Configuration()
|
||||||
|
config.setLocale(LocaleManager.locale)
|
||||||
|
res.updateConfiguration(config, res.displayMetrics)
|
||||||
|
return res.getString(info.labelRes)
|
||||||
|
}
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.loadLabel(pm).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLegalFilename(filename: CharSequence): String {
|
||||||
|
return filename.toString().replace(" ", "_").replace("'", "").replace("\"", "")
|
||||||
|
.replace("$", "").replace("`", "").replace("*", "").replace("/", "_")
|
||||||
|
.replace("#", "").replace("@", "").replace("\\", "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
fun loadModulesLeanback(): Map<String, OldModule> {
|
||||||
|
val moduleMap = ValueSortedMap<String, OldModule>()
|
||||||
|
val path = SuFile(Const.MAGISK_PATH)
|
||||||
|
val modules = path.listFiles { _, name -> name != "lost+found" && name != ".core" }
|
||||||
|
for (file in modules!!) {
|
||||||
|
if (file.isFile) continue
|
||||||
|
val module = OldModule(Const.MAGISK_PATH + "/" + file.name)
|
||||||
|
moduleMap[module.id] = module
|
||||||
|
}
|
||||||
|
return moduleMap
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showSuperUser(): Boolean {
|
||||||
|
return Shell.rootAccess() && (Const.USER_ID == 0
|
||||||
|
|| Config.suMultiuserMode != Config.Value.MULTIUSER_MODE_OWNER_MANAGED)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scheduleUpdateCheck() {
|
||||||
|
if (Config.checkUpdate) {
|
||||||
|
val constraints = Constraints.Builder()
|
||||||
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
|
.setRequiresDeviceIdle(true)
|
||||||
|
.build()
|
||||||
|
val request = PeriodicWorkRequest
|
||||||
|
.Builder(ClassMap[UpdateCheckService::class.java], 12, TimeUnit.HOURS)
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.build()
|
||||||
|
WorkManager.getInstance().enqueueUniquePeriodicWork(
|
||||||
|
Const.ID.CHECK_MAGISK_UPDATE_WORKER_ID,
|
||||||
|
ExistingPeriodicWorkPolicy.REPLACE, request)
|
||||||
|
} else {
|
||||||
|
WorkManager.getInstance().cancelUniqueWork(Const.ID.CHECK_MAGISK_UPDATE_WORKER_ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openLink(context: Context, link: Uri) {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, link)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
if (intent.resolveActivity(context.packageManager) != null) {
|
||||||
|
context.startActivity(intent)
|
||||||
|
} else {
|
||||||
|
toast(R.string.open_link_failed_toast, Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,12 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.utils
|
package com.topjohnwu.magisk.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import com.topjohnwu.magisk.KConfig
|
|
||||||
import com.topjohnwu.magisk.R
|
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -19,36 +13,3 @@ fun ResponseBody.writeToFile(context: Context, fileName: String): File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun ResponseBody.writeToString() = string()
|
fun ResponseBody.writeToString() = string()
|
||||||
|
|
||||||
fun String.launch() = if (KConfig.useCustomTabs) {
|
|
||||||
launchWithCustomTabs()
|
|
||||||
} else {
|
|
||||||
launchWithIntent()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun String.launchWithCustomTabs() {
|
|
||||||
val context: Context by inject()
|
|
||||||
val primaryColor = ContextCompat.getColor(context, R.color.colorPrimary)
|
|
||||||
val secondaryColor = ContextCompat.getColor(context, R.color.colorSecondary)
|
|
||||||
|
|
||||||
CustomTabsIntent.Builder()
|
|
||||||
.enableUrlBarHiding()
|
|
||||||
.setShowTitle(true)
|
|
||||||
.setToolbarColor(primaryColor)
|
|
||||||
.setSecondaryToolbarColor(secondaryColor)
|
|
||||||
.build()
|
|
||||||
.apply { intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK }
|
|
||||||
.launchUrl(context, this.toUri())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.launchWithIntent() {
|
|
||||||
val context: Context by inject()
|
|
||||||
|
|
||||||
Intent(Intent.ACTION_VIEW)
|
|
||||||
.apply {
|
|
||||||
data = toUri()
|
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|
||||||
}
|
|
||||||
.startActivity(context)
|
|
||||||
}
|
|
@ -5,6 +5,8 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.net.Networking;
|
import com.topjohnwu.net.Networking;
|
||||||
import com.topjohnwu.net.ResponseListener;
|
import com.topjohnwu.net.ResponseListener;
|
||||||
@ -12,7 +14,6 @@ import com.topjohnwu.net.ResponseListener;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import ru.noties.markwon.Markwon;
|
import ru.noties.markwon.Markwon;
|
||||||
import ru.noties.markwon.html.HtmlPlugin;
|
import ru.noties.markwon.html.HtmlPlugin;
|
||||||
import ru.noties.markwon.image.ImagesPlugin;
|
import ru.noties.markwon.image.ImagesPlugin;
|
||||||
|
@ -7,19 +7,19 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import androidx.core.app.TaskStackBuilder;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
import com.topjohnwu.magisk.App;
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
import com.topjohnwu.magisk.ClassMap;
|
||||||
import com.topjohnwu.magisk.Config;
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Info;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.model.receiver.GeneralReceiver;
|
import com.topjohnwu.magisk.model.receiver.GeneralReceiver;
|
||||||
import com.topjohnwu.magisk.ui.SplashActivity;
|
import com.topjohnwu.magisk.ui.SplashActivity;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
|
||||||
import androidx.core.app.TaskStackBuilder;
|
|
||||||
|
|
||||||
public class Notifications {
|
public class Notifications {
|
||||||
|
|
||||||
public static NotificationManagerCompat mgr = NotificationManagerCompat.from(App.self);
|
public static NotificationManagerCompat mgr = NotificationManagerCompat.from(App.self);
|
||||||
@ -62,12 +62,12 @@ public class Notifications {
|
|||||||
|
|
||||||
public static void managerUpdate() {
|
public static void managerUpdate() {
|
||||||
App app = App.self;
|
App app = App.self;
|
||||||
String name = Utils.fmt("MagiskManager v%s(%d)",
|
String name = Utils.INSTANCE.fmt("MagiskManager v%s(%d)",
|
||||||
Config.remoteManagerVersionString, Config.remoteManagerVersionCode);
|
Info.remoteManagerVersionString, Info.remoteManagerVersionCode);
|
||||||
|
|
||||||
Intent intent = new Intent(app, ClassMap.get(GeneralReceiver.class));
|
Intent intent = new Intent(app, ClassMap.get(GeneralReceiver.class));
|
||||||
intent.setAction(Const.Key.BROADCAST_MANAGER_UPDATE);
|
intent.setAction(Const.Key.BROADCAST_MANAGER_UPDATE);
|
||||||
intent.putExtra(Const.Key.INTENT_SET_LINK, Config.managerLink);
|
intent.putExtra(Const.Key.INTENT_SET_LINK, Info.managerLink);
|
||||||
intent.putExtra(Const.Key.INTENT_SET_NAME, name);
|
intent.putExtra(Const.Key.INTENT_SET_NAME, name);
|
||||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(app,
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(app,
|
||||||
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
@ -5,13 +5,13 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
import com.topjohnwu.magisk.App;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.net.DownloadProgressListener;
|
import com.topjohnwu.net.DownloadProgressListener;
|
||||||
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
|
|
||||||
public class ProgressNotification implements DownloadProgressListener {
|
public class ProgressNotification implements DownloadProgressListener {
|
||||||
|
|
||||||
private NotificationCompat.Builder builder;
|
private NotificationCompat.Builder builder;
|
||||||
@ -22,7 +22,7 @@ public class ProgressNotification implements DownloadProgressListener {
|
|||||||
builder = Notifications.progress(title);
|
builder = Notifications.progress(title);
|
||||||
prevTime = System.currentTimeMillis();
|
prevTime = System.currentTimeMillis();
|
||||||
update();
|
update();
|
||||||
Utils.toast(App.self.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT);
|
Utils.INSTANCE.toast(App.self.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.ShortcutInfo;
|
|
||||||
import android.content.pm.ShortcutManager;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
|
||||||
import com.topjohnwu.magisk.Config;
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.ui.SplashActivity;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
public class Shortcuts {
|
|
||||||
|
|
||||||
public static void setup(Context context) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
|
||||||
ShortcutManager manager = context.getSystemService(ShortcutManager.class);
|
|
||||||
manager.setDynamicShortcuts(getShortCuts(context));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
|
|
||||||
private static ArrayList<ShortcutInfo> getShortCuts(Context context) {
|
|
||||||
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
|
|
||||||
boolean root = Shell.rootAccess();
|
|
||||||
if (Utils.showSuperUser()) {
|
|
||||||
shortCuts.add(new ShortcutInfo.Builder(context, "superuser")
|
|
||||||
.setShortLabel(context.getString(R.string.superuser))
|
|
||||||
.setIntent(new Intent(context, ClassMap.get(SplashActivity.class))
|
|
||||||
.putExtra(Const.Key.OPEN_SECTION, "superuser")
|
|
||||||
.setAction(Intent.ACTION_VIEW)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
|
||||||
.setIcon(Icon.createWithResource(context, R.drawable.sc_superuser))
|
|
||||||
.setRank(0)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
if (root && (boolean) Config.get(Config.Key.MAGISKHIDE)) {
|
|
||||||
shortCuts.add(new ShortcutInfo.Builder(context, "magiskhide")
|
|
||||||
.setShortLabel(context.getString(R.string.magiskhide))
|
|
||||||
.setIntent(new Intent(context, ClassMap.get(SplashActivity.class))
|
|
||||||
.putExtra(Const.Key.OPEN_SECTION, "magiskhide")
|
|
||||||
.setAction(Intent.ACTION_VIEW)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
|
||||||
.setIcon(Icon.createWithResource(context, R.drawable.sc_magiskhide))
|
|
||||||
.setRank(1)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
if (!(boolean) Config.get(Config.Key.COREONLY) && root && Config.magiskVersionCode >= 0) {
|
|
||||||
shortCuts.add(new ShortcutInfo.Builder(context, "modules")
|
|
||||||
.setShortLabel(context.getString(R.string.modules))
|
|
||||||
.setIntent(new Intent(context, ClassMap.get(SplashActivity.class))
|
|
||||||
.putExtra(Const.Key.OPEN_SECTION, "modules")
|
|
||||||
.setAction(Intent.ACTION_VIEW)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
|
||||||
.setIcon(Icon.createWithResource(context, R.drawable.sc_extension))
|
|
||||||
.setRank(3)
|
|
||||||
.build());
|
|
||||||
shortCuts.add(new ShortcutInfo.Builder(context, "downloads")
|
|
||||||
.setShortLabel(context.getString(R.string.downloads))
|
|
||||||
.setIntent(new Intent(context, ClassMap.get(SplashActivity.class))
|
|
||||||
.putExtra(Const.Key.OPEN_SECTION, "downloads")
|
|
||||||
.setAction(Intent.ACTION_VIEW)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
|
||||||
.setIcon(Icon.createWithResource(context, R.drawable.sc_cloud_download))
|
|
||||||
.setRank(2)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
return shortCuts;
|
|
||||||
}
|
|
||||||
}
|
|
72
app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt
Normal file
72
app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package com.topjohnwu.magisk.view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.ShortcutInfo
|
||||||
|
import android.content.pm.ShortcutManager
|
||||||
|
import android.graphics.drawable.Icon
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import com.topjohnwu.magisk.*
|
||||||
|
import com.topjohnwu.magisk.ui.SplashActivity
|
||||||
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
|
||||||
|
object Shortcuts {
|
||||||
|
|
||||||
|
fun setup(context: Context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
|
val manager = context.getSystemService(ShortcutManager::class.java)
|
||||||
|
manager?.dynamicShortcuts = getShortCuts(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
|
||||||
|
private fun getShortCuts(context: Context): List<ShortcutInfo> {
|
||||||
|
val shortCuts = mutableListOf<ShortcutInfo>()
|
||||||
|
val root = Shell.rootAccess()
|
||||||
|
if (Utils.showSuperUser()) {
|
||||||
|
shortCuts.add(ShortcutInfo.Builder(context, "superuser")
|
||||||
|
.setShortLabel(context.getString(R.string.superuser))
|
||||||
|
.setIntent(Intent(context, ClassMap[SplashActivity::class.java])
|
||||||
|
.putExtra(Const.Key.OPEN_SECTION, "superuser")
|
||||||
|
.setAction(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
|
.setIcon(Icon.createWithResource(context, R.drawable.sc_superuser))
|
||||||
|
.setRank(0)
|
||||||
|
.build())
|
||||||
|
}
|
||||||
|
if (root && Config.magiskHide) {
|
||||||
|
shortCuts.add(ShortcutInfo.Builder(context, "magiskhide")
|
||||||
|
.setShortLabel(context.getString(R.string.magiskhide))
|
||||||
|
.setIntent(Intent(context, ClassMap[SplashActivity::class.java])
|
||||||
|
.putExtra(Const.Key.OPEN_SECTION, "magiskhide")
|
||||||
|
.setAction(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
|
.setIcon(Icon.createWithResource(context, R.drawable.sc_magiskhide))
|
||||||
|
.setRank(1)
|
||||||
|
.build())
|
||||||
|
}
|
||||||
|
if (!Config.coreOnly && root && Info.magiskVersionCode >= 0) {
|
||||||
|
shortCuts.add(ShortcutInfo.Builder(context, "modules")
|
||||||
|
.setShortLabel(context.getString(R.string.modules))
|
||||||
|
.setIntent(Intent(context, ClassMap[SplashActivity::class.java])
|
||||||
|
.putExtra(Const.Key.OPEN_SECTION, "modules")
|
||||||
|
.setAction(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
|
.setIcon(Icon.createWithResource(context, R.drawable.sc_extension))
|
||||||
|
.setRank(3)
|
||||||
|
.build())
|
||||||
|
shortCuts.add(ShortcutInfo.Builder(context, "downloads")
|
||||||
|
.setShortLabel(context.getString(R.string.downloads))
|
||||||
|
.setIntent(Intent(context, ClassMap[SplashActivity::class.java])
|
||||||
|
.putExtra(Const.Key.OPEN_SECTION, "downloads")
|
||||||
|
.setAction(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
|
.setIcon(Icon.createWithResource(context, R.drawable.sc_cloud_download))
|
||||||
|
.setRank(2)
|
||||||
|
.build())
|
||||||
|
}
|
||||||
|
return shortCuts
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,8 @@ import android.app.Activity;
|
|||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.tasks.MagiskInstaller;
|
import com.topjohnwu.magisk.tasks.MagiskInstaller;
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
@ -12,8 +14,6 @@ import com.topjohnwu.superuser.Shell;
|
|||||||
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
public class EnvFixDialog extends CustomAlertDialog {
|
public class EnvFixDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
public EnvFixDialog(@NonNull Activity activity) {
|
public EnvFixDialog(@NonNull Activity activity) {
|
||||||
@ -36,7 +36,7 @@ public class EnvFixDialog extends CustomAlertDialog {
|
|||||||
@Override
|
@Override
|
||||||
protected void onResult(boolean success) {
|
protected void onResult(boolean success) {
|
||||||
pd.dismiss();
|
pd.dismiss();
|
||||||
Utils.toast(success ? R.string.reboot_delay_toast : R.string.setup_fail, Toast.LENGTH_LONG);
|
Utils.INSTANCE.toast(success ? R.string.reboot_delay_toast : R.string.setup_fail, Toast.LENGTH_LONG);
|
||||||
if (success)
|
if (success)
|
||||||
UiThreadHandler.handler.postDelayed(RootUtils::reboot, 5000);
|
UiThreadHandler.handler.postDelayed(RootUtils::reboot, 5000);
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,14 @@ import android.os.Build;
|
|||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
public class FingerprintAuthDialog extends CustomAlertDialog {
|
public class FingerprintAuthDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
@ -31,13 +31,13 @@ public class FingerprintAuthDialog extends CustomAlertDialog {
|
|||||||
super(activity);
|
super(activity);
|
||||||
callback = onSuccess;
|
callback = onSuccess;
|
||||||
Drawable fingerprint = activity.getResources().getDrawable(R.drawable.ic_fingerprint);
|
Drawable fingerprint = activity.getResources().getDrawable(R.drawable.ic_fingerprint);
|
||||||
fingerprint.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50));
|
fingerprint.setBounds(0, 0, Utils.INSTANCE.dpInPx(50), Utils.INSTANCE.dpInPx(50));
|
||||||
Resources.Theme theme = activity.getTheme();
|
Resources.Theme theme = activity.getTheme();
|
||||||
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
|
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
|
||||||
fingerprint.setTint(ta.getColor(0, Color.GRAY));
|
fingerprint.setTint(ta.getColor(0, Color.GRAY));
|
||||||
ta.recycle();
|
ta.recycle();
|
||||||
binding.message.setCompoundDrawables(null, null, null, fingerprint);
|
binding.message.setCompoundDrawables(null, null, null, fingerprint);
|
||||||
binding.message.setCompoundDrawablePadding(Utils.dpInPx(20));
|
binding.message.setCompoundDrawablePadding(Utils.INSTANCE.dpInPx(20));
|
||||||
binding.message.setGravity(Gravity.CENTER);
|
binding.message.setGravity(Gravity.CENTER);
|
||||||
setMessage(R.string.auth_fingerprint);
|
setMessage(R.string.auth_fingerprint);
|
||||||
setNegativeButton(R.string.close, (d, w) -> {
|
setNegativeButton(R.string.close, (d, w) -> {
|
||||||
@ -54,7 +54,9 @@ public class FingerprintAuthDialog extends CustomAlertDialog {
|
|||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
helper = new DialogFingerprintHelper();
|
helper = new DialogFingerprintHelper();
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {
|
||||||
|
ignored.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FingerprintAuthDialog(@NonNull Activity activity, @NonNull Runnable onSuccess, @NonNull Runnable onFailure) {
|
public FingerprintAuthDialog(@NonNull Activity activity, @NonNull Runnable onSuccess, @NonNull Runnable onFailure) {
|
||||||
@ -67,7 +69,7 @@ public class FingerprintAuthDialog extends CustomAlertDialog {
|
|||||||
create();
|
create();
|
||||||
if (helper == null) {
|
if (helper == null) {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
|
Utils.INSTANCE.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
|
||||||
} else {
|
} else {
|
||||||
helper.authenticate();
|
helper.authenticate();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
@ -4,10 +4,12 @@ import android.app.Activity;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
import com.topjohnwu.magisk.ClassMap;
|
||||||
import com.topjohnwu.magisk.Config;
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Info;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.ui.base.IBaseLeanback;
|
import com.topjohnwu.magisk.ui.base.IBaseLeanback;
|
||||||
import com.topjohnwu.magisk.ui.flash.FlashActivity;
|
import com.topjohnwu.magisk.ui.flash.FlashActivity;
|
||||||
@ -19,8 +21,6 @@ import com.topjohnwu.net.Networking;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
|
|
||||||
class InstallMethodDialog extends AlertDialog.Builder {
|
class InstallMethodDialog extends AlertDialog.Builder {
|
||||||
|
|
||||||
<Ctxt extends Activity & IBaseLeanback> InstallMethodDialog(Ctxt activity, List<String> options) {
|
<Ctxt extends Activity & IBaseLeanback> InstallMethodDialog(Ctxt activity, List<String> options) {
|
||||||
@ -50,7 +50,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
|
|||||||
|
|
||||||
private <Ctxt extends Activity & IBaseLeanback> void patchBoot(Ctxt activity) {
|
private <Ctxt extends Activity & IBaseLeanback> void patchBoot(Ctxt activity) {
|
||||||
activity.runWithExternalRW(() -> {
|
activity.runWithExternalRW(() -> {
|
||||||
Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG);
|
Utils.INSTANCE.toast(R.string.patch_file_msg, Toast.LENGTH_LONG);
|
||||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT)
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT)
|
||||||
.setType("*/*")
|
.setType("*/*")
|
||||||
.addCategory(Intent.CATEGORY_OPENABLE);
|
.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
@ -68,11 +68,11 @@ class InstallMethodDialog extends AlertDialog.Builder {
|
|||||||
|
|
||||||
private <Ctxt extends Activity & IBaseLeanback> void downloadOnly(Ctxt activity) {
|
private <Ctxt extends Activity & IBaseLeanback> void downloadOnly(Ctxt activity) {
|
||||||
activity.runWithExternalRW(() -> {
|
activity.runWithExternalRW(() -> {
|
||||||
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
String filename = Utils.INSTANCE.fmt("Magisk-v%s(%d).zip",
|
||||||
Config.remoteMagiskVersionString, Config.remoteMagiskVersionCode);
|
Info.remoteMagiskVersionString, Info.remoteMagiskVersionCode);
|
||||||
File zip = new File(Const.EXTERNAL_PATH, filename);
|
File zip = new File(Const.EXTERNAL_PATH, filename);
|
||||||
ProgressNotification progress = new ProgressNotification(filename);
|
ProgressNotification progress = new ProgressNotification(filename);
|
||||||
Networking.get(Config.magiskLink)
|
Networking.get(Info.magiskLink)
|
||||||
.setDownloadProgressListener(progress)
|
.setDownloadProgressListener(progress)
|
||||||
.setErrorHandler((conn, e) -> progress.dlFail())
|
.setErrorHandler((conn, e) -> progress.dlFail())
|
||||||
.getAsFile(zip, f -> {
|
.getAsFile(zip, f -> {
|
||||||
|
@ -4,7 +4,7 @@ import android.app.Activity;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Config;
|
import com.topjohnwu.magisk.Info;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.ui.base.IBaseLeanback;
|
import com.topjohnwu.magisk.ui.base.IBaseLeanback;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
@ -18,8 +18,8 @@ import java.util.List;
|
|||||||
public class MagiskInstallDialog extends CustomAlertDialog {
|
public class MagiskInstallDialog extends CustomAlertDialog {
|
||||||
public <Ctxt extends Activity & IBaseLeanback> MagiskInstallDialog(Ctxt a) {
|
public <Ctxt extends Activity & IBaseLeanback> MagiskInstallDialog(Ctxt a) {
|
||||||
super(a);
|
super(a);
|
||||||
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
String filename = Utils.INSTANCE.fmt("Magisk-v%s(%d).zip",
|
||||||
Config.remoteMagiskVersionString, Config.remoteMagiskVersionCode);
|
Info.remoteMagiskVersionString, Info.remoteMagiskVersionCode);
|
||||||
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.magisk)));
|
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.magisk)));
|
||||||
setMessage(a.getString(R.string.repo_install_msg, filename));
|
setMessage(a.getString(R.string.repo_install_msg, filename));
|
||||||
setCancelable(true);
|
setCancelable(true);
|
||||||
@ -36,13 +36,13 @@ public class MagiskInstallDialog extends CustomAlertDialog {
|
|||||||
}
|
}
|
||||||
new InstallMethodDialog(a, options).show();
|
new InstallMethodDialog(a, options).show();
|
||||||
});
|
});
|
||||||
if (!TextUtils.isEmpty(Config.magiskNoteLink)) {
|
if (!TextUtils.isEmpty(Info.magiskNoteLink)) {
|
||||||
setNeutralButton(R.string.release_notes, (d, i) -> {
|
setNeutralButton(R.string.release_notes, (d, i) -> {
|
||||||
if (Config.magiskNoteLink.contains("forum.xda-developers")) {
|
if (Info.magiskNoteLink.contains("forum.xda-developers")) {
|
||||||
// Open forum links in browser
|
// Open forum links in browser
|
||||||
Utils.openLink(a, Uri.parse(Config.magiskNoteLink));
|
Utils.INSTANCE.openLink(a, Uri.parse(Info.magiskNoteLink));
|
||||||
} else {
|
} else {
|
||||||
MarkDownWindow.show(a, null, Config.magiskNoteLink);
|
MarkDownWindow.show(a, null, Info.magiskNoteLink);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import android.text.TextUtils;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Config;
|
import com.topjohnwu.magisk.Info;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.DownloadApp;
|
import com.topjohnwu.magisk.utils.DownloadApp;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
@ -15,14 +15,14 @@ public class ManagerInstallDialog extends CustomAlertDialog {
|
|||||||
|
|
||||||
public ManagerInstallDialog(@NonNull Activity a) {
|
public ManagerInstallDialog(@NonNull Activity a) {
|
||||||
super(a);
|
super(a);
|
||||||
String name = Utils.fmt("MagiskManager v%s(%d)",
|
String name = Utils.INSTANCE.fmt("MagiskManager v%s(%d)",
|
||||||
Config.remoteManagerVersionString, Config.remoteManagerVersionCode);
|
Info.remoteManagerVersionString, Info.remoteManagerVersionCode);
|
||||||
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.app_name)));
|
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.app_name)));
|
||||||
setMessage(a.getString(R.string.repo_install_msg, name));
|
setMessage(a.getString(R.string.repo_install_msg, name));
|
||||||
setCancelable(true);
|
setCancelable(true);
|
||||||
setPositiveButton(R.string.install, (d, i) -> DownloadApp.upgrade(name));
|
setPositiveButton(R.string.install, (d, i) -> DownloadApp.upgrade(name));
|
||||||
if (!TextUtils.isEmpty(Config.managerNoteLink)) {
|
if (!TextUtils.isEmpty(Info.managerNoteLink)) {
|
||||||
setNeutralButton(R.string.app_changelog, (d, i) -> MarkDownWindow.show(a, null, Config.managerNoteLink));
|
setNeutralButton(R.string.app_changelog, (d, i) -> MarkDownWindow.show(a, null, Info.managerNoteLink));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,11 @@ import android.net.Uri;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
import com.topjohnwu.magisk.ClassMap;
|
||||||
import com.topjohnwu.magisk.Config;
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Info;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.ui.flash.FlashActivity;
|
import com.topjohnwu.magisk.ui.flash.FlashActivity;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
@ -19,8 +21,6 @@ import com.topjohnwu.superuser.Shell;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
public class UninstallDialog extends CustomAlertDialog {
|
public class UninstallDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
public UninstallDialog(@NonNull Activity activity) {
|
public UninstallDialog(@NonNull Activity activity) {
|
||||||
@ -34,17 +34,17 @@ public class UninstallDialog extends CustomAlertDialog {
|
|||||||
Shell.su("restore_imgs").submit(result -> {
|
Shell.su("restore_imgs").submit(result -> {
|
||||||
dialog.cancel();
|
dialog.cancel();
|
||||||
if (result.isSuccess()) {
|
if (result.isSuccess()) {
|
||||||
Utils.toast(R.string.restore_done, Toast.LENGTH_SHORT);
|
Utils.INSTANCE.toast(R.string.restore_done, Toast.LENGTH_SHORT);
|
||||||
} else {
|
} else {
|
||||||
Utils.toast(R.string.restore_fail, Toast.LENGTH_LONG);
|
Utils.INSTANCE.toast(R.string.restore_fail, Toast.LENGTH_LONG);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (!TextUtils.isEmpty(Config.uninstallerLink)) {
|
if (!TextUtils.isEmpty(Info.uninstallerLink)) {
|
||||||
setPositiveButton(R.string.complete_uninstall, (d, i) -> {
|
setPositiveButton(R.string.complete_uninstall, (d, i) -> {
|
||||||
File zip = new File(activity.getFilesDir(), "uninstaller.zip");
|
File zip = new File(activity.getFilesDir(), "uninstaller.zip");
|
||||||
ProgressNotification progress = new ProgressNotification(zip.getName());
|
ProgressNotification progress = new ProgressNotification(zip.getName());
|
||||||
Networking.get(Config.uninstallerLink)
|
Networking.get(Info.uninstallerLink)
|
||||||
.setDownloadProgressListener(progress)
|
.setDownloadProgressListener(progress)
|
||||||
.setErrorHandler(((conn, e) -> progress.dlFail()))
|
.setErrorHandler(((conn, e) -> progress.dlFail()))
|
||||||
.getAsFile(zip, f -> {
|
.getAsFile(zip, f -> {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:key="dark_theme"
|
android:key="dark_theme"
|
||||||
|
android:defaultValue="true"
|
||||||
android:title="@string/settings_dark_theme_title"
|
android:title="@string/settings_dark_theme_title"
|
||||||
android:summary="@string/settings_dark_theme_summary" />
|
android:summary="@string/settings_dark_theme_summary" />
|
||||||
|
|
||||||
@ -95,28 +96,33 @@
|
|||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="su_auto_response"
|
android:key="su_auto_response"
|
||||||
android:title="@string/auto_response"
|
android:title="@string/auto_response"
|
||||||
|
android:defaultValue="0"
|
||||||
android:entries="@array/auto_response"
|
android:entries="@array/auto_response"
|
||||||
android:entryValues="@array/value_array" />
|
android:entryValues="@array/value_array" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="su_request_timeout"
|
android:key="su_request_timeout"
|
||||||
android:title="@string/request_timeout"
|
android:title="@string/request_timeout"
|
||||||
|
android:defaultValue="10"
|
||||||
android:entries="@array/request_timeout"
|
android:entries="@array/request_timeout"
|
||||||
android:entryValues="@array/request_timeout_value" />
|
android:entryValues="@array/request_timeout_value" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="su_notification"
|
android:key="su_notification"
|
||||||
android:title="@string/superuser_notification"
|
android:title="@string/superuser_notification"
|
||||||
|
android:defaultValue="1"
|
||||||
android:entries="@array/su_notification"
|
android:entries="@array/su_notification"
|
||||||
android:entryValues="@array/value_array" />
|
android:entryValues="@array/value_array" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:key="su_fingerprint"
|
android:key="su_fingerprint"
|
||||||
|
android:defaultValue="false"
|
||||||
android:title="@string/settings_su_fingerprint_title"
|
android:title="@string/settings_su_fingerprint_title"
|
||||||
android:summary="@string/settings_su_fingerprint_summary"/>
|
android:summary="@string/settings_su_fingerprint_summary"/>
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:key="su_reauth"
|
android:key="su_reauth"
|
||||||
|
android:defaultValue="false"
|
||||||
android:title="@string/settings_su_reauth_title"
|
android:title="@string/settings_su_reauth_title"
|
||||||
android:summary="@string/settings_su_reauth_summary"/>
|
android:summary="@string/settings_su_reauth_summary"/>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user