diff --git a/app/src/main/java/com/topjohnwu/magisk/Config.java b/app/src/main/java/com/topjohnwu/magisk/Config.java deleted file mode 100644 index 7a583c912..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/Config.java +++ /dev/null @@ -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 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 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 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; - } - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/Config.kt b/app/src/main/java/com/topjohnwu/magisk/Config.kt new file mode 100644 index 000000000..8502a5f99 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/Config.kt @@ -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(Protected).filesDir.parent}/shared_prefs", + "${packageName}_preferences.xml") + Shell.su("cat $xml > /data/adb/${Const.MANAGER_CONFIGS}").exec() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/ConfigLeanback.kt b/app/src/main/java/com/topjohnwu/magisk/ConfigLeanback.kt deleted file mode 100644 index 6fbf6a069..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/ConfigLeanback.kt +++ /dev/null @@ -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() - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/Const.kt b/app/src/main/java/com/topjohnwu/magisk/Const.kt index 96cc094fa..826a4fa26 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Const.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Const.kt @@ -19,7 +19,6 @@ object Const { const val MAGISK_LOG = "/cache/magisk.log" // Versions - const val UPDATE_SERVICE_VER = 1 const val SNET_EXT_VER = 12 const val SNET_REVISION = "b66b1a914978e5f4c4bbfd74a59f4ad371bac107" const val BOOTCTL_REVISION = "9c5dfc1b8245c0b5b524901ef0ff0f8335757b77" diff --git a/app/src/main/java/com/topjohnwu/magisk/Info.java b/app/src/main/java/com/topjohnwu/magisk/Info.java new file mode 100644 index 000000000..338a10d85 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/Info.java @@ -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) { + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/KConfig.kt b/app/src/main/java/com/topjohnwu/magisk/KConfig.kt deleted file mode 100644 index a481a935e..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/KConfig.kt +++ /dev/null @@ -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 - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/data/database/LogDao.kt b/app/src/main/java/com/topjohnwu/magisk/data/database/LogDao.kt index 1bb589d2a..5e6bfb782 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/database/LogDao.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/database/LogDao.kt @@ -1,6 +1,5 @@ package com.topjohnwu.magisk.data.database -import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.data.database.base.* import com.topjohnwu.magisk.model.entity.MagiskLog import com.topjohnwu.magisk.model.entity.toLog @@ -12,7 +11,7 @@ class LogDao : BaseDao() { override val table = DatabaseDefinition.Table.LOG fun deleteOutdated( - suTimeout: Long = Config.suLogTimeout * TimeUnit.DAYS.toMillis(1) + suTimeout: Long = TimeUnit.DAYS.toMillis(14) ) = query { condition { lessThan("time", suTimeout.toString()) diff --git a/app/src/main/java/com/topjohnwu/magisk/data/database/RepoDatabaseHelper.java b/app/src/main/java/com/topjohnwu/magisk/data/database/RepoDatabaseHelper.java deleted file mode 100644 index 6e4cb35d0..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/data/database/RepoDatabaseHelper.java +++ /dev/null @@ -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 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 getRepoIDSet() { - HashSet 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; - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/data/database/RepoDatabaseHelper.kt b/app/src/main/java/com/topjohnwu/magisk/data/database/RepoDatabaseHelper.kt new file mode 100644 index 000000000..84fc236ad --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/data/database/RepoDatabaseHelper.kt @@ -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 + @Deprecated("") + get() { + val set = HashSet(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) { + 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" + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/data/network/GithubRawApiServices.kt b/app/src/main/java/com/topjohnwu/magisk/data/network/GithubRawApiServices.kt index 603247eab..262bab4ce 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/network/GithubRawApiServices.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/network/GithubRawApiServices.kt @@ -1,7 +1,6 @@ package com.topjohnwu.magisk.data.network import com.topjohnwu.magisk.Const -import com.topjohnwu.magisk.KConfig import com.topjohnwu.magisk.model.entity.MagiskConfig import io.reactivex.Single import okhttp3.ResponseBody @@ -16,19 +15,19 @@ interface GithubRawApiServices { //region topjohnwu/magisk_files @GET("$MAGISK_FILES/master/stable.json") - fun fetchConfig(): Single + fun fetchStableUpdate(): Single @GET("$MAGISK_FILES/master/beta.json") - fun fetchBetaConfig(): Single + fun fetchBetaUpdate(): Single @GET("$MAGISK_FILES/master/canary_builds/release.json") - fun fetchCanaryConfig(): Single + fun fetchCanaryUpdate(): Single @GET("$MAGISK_FILES/master/canary_builds/canary.json") - fun fetchCanaryDebugConfig(): Single + fun fetchCanaryDebugUpdate(): Single @GET - fun fetchCustomConfig(@Url url: String): Single + fun fetchCustomUpdate(@Url url: String): Single @GET("$MAGISK_FILES/{$REVISION}/snet.apk") @Streaming @@ -55,7 +54,7 @@ interface GithubRawApiServices { 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_MODULES = "Magisk-Modules-Repo" } diff --git a/app/src/main/java/com/topjohnwu/magisk/data/repository/DBConfig.kt b/app/src/main/java/com/topjohnwu/magisk/data/repository/DBConfig.kt new file mode 100644 index 000000000..7a0274c3b --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/data/repository/DBConfig.kt @@ -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 { + + 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 { + + 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 { + + 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() + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/data/repository/MagiskRepository.kt b/app/src/main/java/com/topjohnwu/magisk/data/repository/MagiskRepository.kt index 146b5f291..88549261a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/repository/MagiskRepository.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/repository/MagiskRepository.kt @@ -4,20 +4,17 @@ import android.content.Context import android.content.pm.PackageManager import com.topjohnwu.magisk.App 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.suRaw import com.topjohnwu.magisk.data.network.GithubRawApiServices import com.topjohnwu.magisk.model.entity.HideAppInfo 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.inject import com.topjohnwu.magisk.utils.toSingle import com.topjohnwu.magisk.utils.writeToFile import com.topjohnwu.superuser.Shell import io.reactivex.Single -import io.reactivex.functions.BiFunction class MagiskRepository( private val context: Context, @@ -25,15 +22,15 @@ class MagiskRepository( private val packageManager: PackageManager ) { - fun fetchMagisk() = fetchConfig() + fun fetchMagisk() = fetchUpdate() .flatMap { apiRaw.fetchFile(it.magisk.link) } .map { it.writeToFile(context, FILE_MAGISK_ZIP) } - fun fetchManager() = fetchConfig() + fun fetchManager() = fetchUpdate() .flatMap { apiRaw.fetchFile(it.app.link) } .map { it.writeToFile(context, FILE_MAGISK_APK) } - fun fetchUninstaller() = fetchConfig() + fun fetchUninstaller() = fetchUpdate() .flatMap { apiRaw.fetchFile(it.uninstaller.link) } .map { it.writeToFile(context, FILE_UNINSTALLER_ZIP) } @@ -44,23 +41,35 @@ class MagiskRepository( .map { it.writeToFile(context, FILE_BOOTCTL_SH) } - fun fetchConfig() = when (KConfig.updateChannel) { - KConfig.UpdateChannel.STABLE -> apiRaw.fetchConfig() - KConfig.UpdateChannel.BETA -> apiRaw.fetchBetaConfig() - KConfig.UpdateChannel.CANARY -> apiRaw.fetchCanaryConfig() - KConfig.UpdateChannel.CANARY_DEBUG -> apiRaw.fetchCanaryDebugConfig() - KConfig.UpdateChannel.CUSTOM -> apiRaw.fetchCustomConfig(KConfig.customUpdateChannel) - } - .doOnSuccess { - Config.remoteMagiskVersionCode = it.magisk.versionCode.toIntOrNull() ?: -1 - Config.magiskLink = it.magisk.link - Config.magiskNoteLink = it.magisk.note - Config.magiskMD5 = it.magisk.hash - Config.remoteManagerVersionCode = it.app.versionCode.toIntOrNull() ?: -1 - Config.remoteManagerVersionString = it.app.version - Config.managerLink = it.app.link - Config.managerNoteLink = it.app.note - Config.uninstallerLink = it.uninstaller.link + fun fetchUpdate() = when (Config.updateChannel) { + Config.Value.DEFAULT_CHANNEL, Config.Value.STABLE_CHANNEL -> apiRaw.fetchStableUpdate() + Config.Value.BETA_CHANNEL -> apiRaw.fetchBetaUpdate() + Config.Value.CANARY_CHANNEL -> apiRaw.fetchCanaryUpdate() + Config.Value.CANARY_DEBUG_CHANNEL -> apiRaw.fetchCanaryDebugUpdate() + Config.Value.CUSTOM_CHANNEL -> apiRaw.fetchCustomUpdate(Config.customChannelUrl) + else -> throw IllegalArgumentException() + }.flatMap { + // If remote version is lower than current installed, try switching to beta + if (it.magisk.versionCode.toIntOrNull() ?: -1 < Info.magiskVersionCode + && Config.updateChannel == Config.Value.DEFAULT_CHANNEL) { + Config.updateChannel = Config.Value.BETA_CHANNEL + apiRaw.fetchBetaUpdate() + } else { + Single.just(it) + } + }.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() = diff --git a/app/src/main/java/com/topjohnwu/magisk/data/repository/SettingRepository.kt b/app/src/main/java/com/topjohnwu/magisk/data/repository/SettingRepository.kt deleted file mode 100644 index e8535730c..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/data/repository/SettingRepository.kt +++ /dev/null @@ -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) - -} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/data/repository/StringRepository.kt b/app/src/main/java/com/topjohnwu/magisk/data/repository/StringRepository.kt deleted file mode 100644 index 70fd9ef69..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/data/repository/StringRepository.kt +++ /dev/null @@ -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) - -} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/di/RepositoryModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/RepositoryModule.kt index fa2584c1b..55de36521 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/RepositoryModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/RepositoryModule.kt @@ -8,6 +8,4 @@ val repositoryModule = module { single { MagiskRepository(get(), get(), get()) } single { LogRepository(get()) } single { AppRepository(get()) } - single { SettingRepository(get()) } - single { StringRepository(get()) } } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadModuleService.java b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadModuleService.java index 324b29734..ceb3ea494 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadModuleService.java +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadModuleService.java @@ -6,6 +6,8 @@ import android.content.Intent; import android.net.Uri; import android.os.IBinder; +import androidx.annotation.Nullable; + import com.topjohnwu.magisk.App; import com.topjohnwu.magisk.ClassMap; import com.topjohnwu.magisk.Const; @@ -29,8 +31,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; -import androidx.annotation.Nullable; - public class DownloadModuleService extends Service { private List notifications; diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/BaseModule.java b/app/src/main/java/com/topjohnwu/magisk/model/entity/BaseModule.java index 286eab295..b9bb1ae9a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/BaseModule.java +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/BaseModule.java @@ -5,10 +5,10 @@ import android.database.Cursor; import android.os.Parcel; import android.os.Parcelable; -import java.util.List; - import androidx.annotation.NonNull; +import java.util.List; + public abstract class BaseModule implements Comparable, Parcelable { private String mId, mName, mVersion, mAuthor, mDescription; diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/Policy.java b/app/src/main/java/com/topjohnwu/magisk/model/entity/Policy.java index 10cc6dac6..cb7797e40 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/Policy.java +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/Policy.java @@ -4,10 +4,10 @@ import android.content.ContentValues; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import com.topjohnwu.magisk.utils.Utils; - import androidx.annotation.NonNull; +import com.topjohnwu.magisk.utils.Utils; + public class Policy implements Comparable{ public static final int INTERACTIVE = 0; @@ -27,7 +27,7 @@ public class Policy implements Comparable{ this.uid = uid; packageName = pkgs[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 { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/Repo.java b/app/src/main/java/com/topjohnwu/magisk/model/entity/Repo.java index f72b45a62..853d013f3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/Repo.java +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/Repo.java @@ -54,7 +54,7 @@ public class Repo extends BaseModule { } public void update() throws IllegalRepoException { - String[] props = Utils.dlString(getPropUrl()).split("\\n"); + String[] props = Utils.INSTANCE.dlString(getPropUrl()).split("\\n"); try { parseProps(props); } catch (NumberFormatException e) { @@ -103,7 +103,7 @@ public class Repo extends BaseModule { } public String getDownloadFilename() { - return Utils.getLegalFilename(getName() + "-" + getVersion() + ".zip"); + return Utils.INSTANCE.getLegalFilename(getName() + "-" + getVersion() + ".zip"); } public class IllegalRepoException extends Exception { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/preference/PreferenceModel.kt b/app/src/main/java/com/topjohnwu/magisk/model/preference/PreferenceModel.kt index d751baa98..75c9bbdd2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/preference/PreferenceModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/preference/PreferenceModel.kt @@ -2,6 +2,8 @@ package com.topjohnwu.magisk.model.preference import android.content.Context import android.content.SharedPreferences +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty interface PreferenceModel { @@ -14,6 +16,20 @@ interface PreferenceModel { val prefs: SharedPreferences get() = context.getSharedPreferences(fileName, Context.MODE_PRIVATE) + fun preferenceStrInt( + name: String, + default: Int, + writeDefault: Boolean = false, + commit: Boolean = commitPrefs + ) = object: ReadWriteProperty { + 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( name: String, default: Boolean, diff --git a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt index 43eae3da1..4807fdb62 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt @@ -6,6 +6,7 @@ import android.content.Intent import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const +import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.data.database.base.su import com.topjohnwu.magisk.data.repository.AppRepository 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.SuLogger import com.topjohnwu.magisk.utils.inject -import com.topjohnwu.magisk.utils.get import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.superuser.Shell @@ -64,9 +64,8 @@ open class GeneralReceiver : BroadcastReceiver() { } Intent.ACTION_PACKAGE_REPLACED -> // This will only work pre-O - if (Config.get(Config.Key.SU_REAUTH)!!) { + if (Config.suReAuth) appRepo.delete(getPkg(intent)).blockingGet() - } Intent.ACTION_PACKAGE_FULLY_REMOVED -> { val pkg = getPkg(intent) appRepo.delete(pkg).blockingGet() @@ -74,7 +73,7 @@ open class GeneralReceiver : BroadcastReceiver() { } Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setup(context) 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)) } Const.Key.BROADCAST_REBOOT -> RootUtils.reboot() diff --git a/app/src/main/java/com/topjohnwu/magisk/model/update/UpdateCheckService.kt b/app/src/main/java/com/topjohnwu/magisk/model/update/UpdateCheckService.kt index 9c191ca43..a69d5eef9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/update/UpdateCheckService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/update/UpdateCheckService.kt @@ -2,7 +2,7 @@ package com.topjohnwu.magisk.model.update import androidx.work.ListenableWorker 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.model.worker.DelegateWorker import com.topjohnwu.magisk.utils.inject @@ -14,10 +14,10 @@ class UpdateCheckService : DelegateWorker() { override fun doWork(): ListenableWorker.Result { return runCatching { - magiskRepo.fetchConfig().blockingGet() - if (BuildConfig.VERSION_CODE < Config.remoteManagerVersionCode) + magiskRepo.fetchUpdate().blockingGet() + if (BuildConfig.VERSION_CODE < Info.remoteManagerVersionCode) Notifications.managerUpdate() - else if (Config.magiskVersionCode < Config.remoteManagerVersionCode) + else if (Info.magiskVersionCode < Info.remoteManagerVersionCode) Notifications.magiskUpdate() ListenableWorker.Result.success() }.getOrElse { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/worker/DelegateWorker.java b/app/src/main/java/com/topjohnwu/magisk/model/worker/DelegateWorker.java index 670e88d1c..f9460e824 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/worker/DelegateWorker.java +++ b/app/src/main/java/com/topjohnwu/magisk/model/worker/DelegateWorker.java @@ -4,12 +4,6 @@ import android.content.Context; import android.net.Network; 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.NonNull; import androidx.annotation.Nullable; @@ -17,6 +11,12 @@ import androidx.annotation.RequiresApi; import androidx.work.Data; 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 { private ListenableWorker worker; diff --git a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.java b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.java index f958aabd4..cc867b1bd 100644 --- a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.java +++ b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.java @@ -8,8 +8,8 @@ import androidx.annotation.MainThread; import androidx.annotation.WorkerThread; import com.topjohnwu.magisk.App; -import com.topjohnwu.magisk.Config; import com.topjohnwu.magisk.Const; +import com.topjohnwu.magisk.Info; import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.net.DownloadProgressListener; import com.topjohnwu.net.Networking; @@ -123,9 +123,9 @@ public abstract class MagiskInstaller { 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"); - Networking.get(Config.magiskLink) + Networking.get(Info.magiskLink) .setDownloadProgressListener(new ProgressLog()) .execForFile(zip); } else { @@ -282,10 +282,10 @@ public abstract class MagiskInstaller { return false; } - if (!Shell.sh(Utils.fmt( + if (!Shell.sh(Utils.INSTANCE.fmt( "KEEPFORCEENCRYPT=%b KEEPVERITY=%b RECOVERYMODE=%b " + "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()) return false; @@ -311,10 +311,10 @@ public abstract class MagiskInstaller { } 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()) return false; - if (!Config.keepVerity) + if (!Info.keepVerity) Shell.su("patch_dtbo_image").to(console, logs).exec(); return true; } diff --git a/app/src/main/java/com/topjohnwu/magisk/tasks/UpdateRepos.java b/app/src/main/java/com/topjohnwu/magisk/tasks/UpdateRepos.java index 4098bb497..b7b5eaceb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/tasks/UpdateRepos.java +++ b/app/src/main/java/com/topjohnwu/magisk/tasks/UpdateRepos.java @@ -3,6 +3,8 @@ package com.topjohnwu.magisk.tasks; import android.database.Cursor; import android.util.Pair; +import androidx.annotation.NonNull; + import com.topjohnwu.magisk.App; import com.topjohnwu.magisk.Config; import com.topjohnwu.magisk.Const; @@ -31,7 +33,6 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import androidx.annotation.NonNull; import io.reactivex.Single; @Deprecated @@ -74,10 +75,10 @@ public class UpdateRepos { * first page is updated to determine whether the online repo database is changed */ 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) { - String etag = Config.get(Config.Key.ETAG_KEY); - if (etag != null) + String etag = Config.getEtagKey(); + if (!etag.isEmpty()) req.addHeaders(Const.Key.IF_NONE_MATCH, etag); } Request.Result res = req.execForJSONArray(); @@ -110,7 +111,7 @@ public class UpdateRepos { String etag = res.getConnection().getHeaderField(Config.Key.ETAG_KEY); if (etag != null) { etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1); - Config.set(Config.Key.ETAG_KEY, etag); + Config.setEtagKey(etag); } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index 1aa718252..654a1667e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -7,6 +7,7 @@ import androidx.fragment.app.Fragment import com.topjohnwu.magisk.ClassMap import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const.Key.OPEN_SECTION +import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.ActivityMainBinding import com.topjohnwu.magisk.model.navigation.Navigation @@ -105,11 +106,11 @@ open class MainActivity : MagiskActivity() { private fun checkHideSection() { val menu = binding.navView.menu menu.findItem(R.id.magiskHideFragment).isVisible = - Shell.rootAccess() && Config.get(Config.Key.MAGISKHIDE) as Boolean + Shell.rootAccess() && Config.magiskHide menu.findItem(R.id.modulesFragment).isVisible = - Shell.rootAccess() && Config.magiskVersionCode >= 0 + Shell.rootAccess() && Info.magiskVersionCode >= 0 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 = Shell.rootAccess() menu.findItem(R.id.superuserFragment).isVisible = diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index 0d48f53fb..db3259245 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -7,6 +7,7 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import com.skoumal.teanity.extensions.subscribeK import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.data.database.SettingsDao import com.topjohnwu.magisk.tasks.UpdateRepos import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.Notifications @@ -21,7 +22,7 @@ open class SplashActivity : AppCompatActivity() { super.onCreate(savedInstanceState) 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) .setTitle(R.string.unsupport_magisk_title) .setMessage(R.string.unsupport_magisk_message) @@ -35,9 +36,9 @@ open class SplashActivity : AppCompatActivity() { } private fun initAndStart() { - val pkg = Config.get(Config.Key.SU_MANAGER) - if (pkg != null && packageName == BuildConfig.APPLICATION_ID) { - Config.remove(Config.Key.SU_MANAGER) + val pkg = Config.suManager + if (Config.suManager.isNotEmpty() && packageName == BuildConfig.APPLICATION_ID) { + get().delete(Config.Key.SU_MANAGER) Shell.su("pm uninstall $pkg").submit() } if (TextUtils.equals(pkg, packageName)) { @@ -56,15 +57,12 @@ open class SplashActivity : AppCompatActivity() { // Schedule periodic update checks Utils.scheduleUpdateCheck() - //CheckUpdates.check() // Setup shortcuts Shortcuts.setup(this) // Magisk working as expected - if (Shell.rootAccess() && Config.magiskVersionCode > 0) { - // Load modules - //Utils.loadModules(false) + if (Shell.rootAccess() && Info.magiskVersionCode > 0) { // Load repos if (Networking.checkNetworkStatus(this)) { get().exec().subscribeK() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt index 6f9bb3388..5095ed8f8 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.kt @@ -12,8 +12,6 @@ import androidx.core.view.isVisible import androidx.preference.* import androidx.recyclerview.widget.RecyclerView import com.topjohnwu.magisk.App -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.KConfig import com.topjohnwu.magisk.R import org.koin.android.ext.android.inject @@ -63,22 +61,6 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat(), 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 findPref(key: CharSequence): T { return findPreference(key) as T } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt index 62bf24d25..f70398d18 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt @@ -51,14 +51,12 @@ abstract class MagiskActivity(Config.Key.DARK_THEME) - val theme = if (isDarkTheme) { + val theme = if (Config.darkTheme) { AppCompatDelegate.MODE_NIGHT_YES } else { AppCompatDelegate.MODE_NIGHT_NO } AppCompatDelegate.setDefaultNightMode(theme) - AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) } override fun applyOverrideConfiguration(config: Configuration?) { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt index 64f5f0885..9d703b3a4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt @@ -28,7 +28,7 @@ class MagiskHideFragment : MagiskFragment(Config.Key.SHOW_SYSTEM_APP) + val showSystem = Config.showSystemApp findItem(R.id.show_system).isChecked = showSystem viewModel.isShowSystem.value = showSystem @@ -39,10 +39,8 @@ class MagiskHideFragment : MagiskFragment(), private fun installMagisk() { // Show Manager update first - if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) { + if (Info.remoteManagerVersionCode > BuildConfig.VERSION_CODE) { installManager() return } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index 81ba98c8d..ae429b6d3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -4,10 +4,7 @@ import com.skoumal.teanity.extensions.addOnPropertyChangedCallback import com.skoumal.teanity.extensions.doOnSubscribeUi import com.skoumal.teanity.extensions.subscribeK import com.skoumal.teanity.util.KObservableField -import com.topjohnwu.magisk.BuildConfig -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.Const -import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.* import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.model.events.* import com.topjohnwu.magisk.model.observer.Observer @@ -25,8 +22,8 @@ class HomeViewModel( val isAdvancedExpanded = KObservableField(false) - val isForceEncryption = KObservableField(Config.keepEnc) - val isKeepVerity = KObservableField(Config.keepVerity) + val isForceEncryption = KObservableField(Info.keepEnc) + val isKeepVerity = KObservableField(Info.keepVerity) val magiskState = KObservableField(MagiskState.LOADING) val magiskStateText = Observer(magiskState) { @@ -41,7 +38,7 @@ class HomeViewModel( val magiskCurrentVersion = KObservableField("") val magiskLatestVersion = KObservableField("") val magiskAdditionalInfo = Observer(magiskState) { - if (Config.get(Config.Key.COREONLY)) + if (Config.coreOnly) R.string.core_only_enabled.res() else "" @@ -87,10 +84,10 @@ class HomeViewModel( init { isForceEncryption.addOnPropertyChangedCallback { - Config.keepEnc = it ?: return@addOnPropertyChangedCallback + Info.keepEnc = it ?: return@addOnPropertyChangedCallback } isKeepVerity.addOnPropertyChangedCallback { - Config.keepVerity = it ?: return@addOnPropertyChangedCallback + Info.keepVerity = it ?: return@addOnPropertyChangedCallback } refresh() @@ -154,7 +151,7 @@ class HomeViewModel( } fun refresh() { - magiskRepo.fetchConfig() + magiskRepo.fetchUpdate() .applyViewModel(this) .doOnSubscribeUi { magiskState.value = MagiskState.LOADING @@ -164,14 +161,6 @@ class HomeViewModel( safetyNetTitle.value = R.string.safetyNet_check_text } .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() ensureEnv() } @@ -181,22 +170,22 @@ class HomeViewModel( private fun updateSelf() { state = State.LOADED - magiskState.value = when (Config.magiskVersionCode) { + magiskState.value = when (Info.magiskVersionCode) { 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 } magiskCurrentVersion.value = if (magiskState.value != MagiskState.NOT_INSTALLED) { - version.format(Config.magiskVersionString, Config.magiskVersionCode) + version.format(Info.magiskVersionString, Info.magiskVersionCode) } else { "" } 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 (BuildConfig.VERSION_CODE + 1)..Int.MAX_VALUE -> MagiskState.OBSOLETE else -> MagiskState.UP_TO_DATE @@ -206,7 +195,7 @@ class HomeViewModel( .format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE) managerLatestVersion.value = version - .format(Config.remoteManagerVersionString, Config.remoteManagerVersionCode) + .format(Info.remoteManagerVersionString, Info.remoteManagerVersionCode) } private fun ensureEnv() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt index 61e4c57f8..7cb14796f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt @@ -53,9 +53,9 @@ class ReposFragment : MagiskFragment(), .setTitle(R.string.sorting_order) .setSingleChoiceItems( R.array.sorting_orders, - Config.get(Config.Key.REPO_ORDER)!! + Config.repoOrder ) { d, which -> - Config.set(Config.Key.REPO_ORDER, which) + Config.repoOrder = which viewModel.refresh(false) d.dismiss() }.show() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index 500493e1d..f97164104 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -13,10 +13,11 @@ import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.SwitchPreferenceCompat import com.skoumal.teanity.extensions.subscribeK -import com.topjohnwu.magisk.* -import com.topjohnwu.magisk.KConfig.UpdateChannel +import com.topjohnwu.magisk.BuildConfig +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.repository.SettingRepository import com.topjohnwu.magisk.ui.base.BasePreferenceFragment import com.topjohnwu.magisk.utils.* import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog @@ -27,7 +28,6 @@ import org.koin.android.ext.android.inject class SettingsFragment : BasePreferenceFragment() { private val repoDatabase: RepoDatabaseHelper by inject() - private val settingRepo: SettingRepository by inject() private lateinit var updateChannel: ListPreference private lateinit var autoRes: ListPreference @@ -53,7 +53,7 @@ class SettingsFragment : BasePreferenceFragment() { requestTimeout = findPref(Config.Key.SU_REQUEST_TIMEOUT) suNotification = findPref(Config.Key.SU_NOTIFICATION) 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 fingerprint = findPreference(Config.Key.SU_FINGERPRINT) as SwitchPreferenceCompat val generalCatagory = findPreference("general") as PreferenceCategory @@ -70,7 +70,9 @@ class SettingsFragment : BasePreferenceFragment() { true } findPreference("clear").setOnPreferenceClickListener { - prefs.edit().remove(Config.Key.ETAG_KEY).apply() + prefs.edit { + remove(Config.Key.ETAG_KEY) + } repoDatabase.clearRepo() Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT) true @@ -81,30 +83,23 @@ class SettingsFragment : BasePreferenceFragment() { true } - prefs.edit { - putBoolean(Config.Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint()) - } - updateChannel.setOnPreferenceChangeListener { _, value -> val channel = Integer.parseInt(value as String) + val previous = Config.updateChannel - val previousUpdateChannel = KConfig.updateChannel - val updateChannel = getChannelCompat(channel) - - KConfig.updateChannel = updateChannel - - if (updateChannel === UpdateChannel.CUSTOM) { - val v = LayoutInflater.from(requireActivity()).inflate(R.layout.custom_channel_dialog, null) + if (channel == Config.Value.CUSTOM_CHANNEL) { + val v = LayoutInflater.from(requireActivity()) + .inflate(R.layout.custom_channel_dialog, null) val url = v.findViewById(R.id.custom_url) - url.setText(KConfig.customUpdateChannel) + url.setText(Config.customChannelUrl) AlertDialog.Builder(requireActivity()) .setTitle(R.string.settings_update_custom) .setView(v) .setPositiveButton(R.string.ok) { _, _ -> - setCustomUpdateChannel(url.text.toString()) } + Config.customChannelUrl = url.text.toString() } .setNegativeButton(R.string.close) { _, _ -> - KConfig.updateChannel = previousUpdateChannel } - .setOnCancelListener { KConfig.updateChannel = previousUpdateChannel } + Config.updateChannel = previous } + .setOnCancelListener { Config.updateChannel = previous } .show() } true @@ -114,7 +109,7 @@ class SettingsFragment : BasePreferenceFragment() { /* We only show canary channels if user is already on canary channel * or the user have already chosen canary channel */ - if (!Utils.isCanary() && Config.get(Config.Key.UPDATE_CHANNEL) < Config.Value.CANARY_CHANNEL) { + if (!Utils.isCanary && Config.updateChannel < Config.Value.CANARY_CHANNEL) { // Remove the last 2 entries val entries = updateChannel.entries updateChannel.entries = entries.copyOf(entries.size - 2) @@ -168,8 +163,9 @@ class SettingsFragment : BasePreferenceFragment() { override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String) { when (key) { - Config.Key.ROOT_ACCESS, Config.Key.SU_MULTIUSER_MODE, Config.Key.SU_MNT_NS -> - settingRepo.put(key, Utils.getPrefsInt(prefs, key)).subscribe() + Config.Key.ROOT_ACCESS -> Config.rootMode = Utils.getPrefsInt(prefs, key) + 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.COREONLY -> { if (prefs.getBoolean(key, false)) { @@ -196,14 +192,13 @@ class SettingsFragment : BasePreferenceFragment() { } override fun onPreferenceTreeClick(preference: Preference): Boolean { - val key = preference.key - when (key) { + when (preference.key) { Config.Key.SU_FINGERPRINT -> { val checked = (preference as SwitchPreferenceCompat).isChecked preference.isChecked = !checked FingerprintAuthDialog(requireActivity()) { preference.isChecked = checked - Config.set(key, checked) + Config.suFingerprint = checked }.show() } } @@ -237,34 +232,34 @@ class SettingsFragment : BasePreferenceFragment() { private fun setSummary(key: String) { 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 -> { - var ch = Config.get(key) + var ch = Config.updateChannel ch = if (ch < 0) Config.Value.STABLE_CHANNEL else ch updateChannel.summary = resources .getStringArray(R.array.update_channel)[ch] } - Config.Key.ROOT_ACCESS -> rootConfig.summary = resources - .getStringArray(R.array.su_access)[Config.get(key)] Config.Key.SU_AUTO_RESPONSE -> autoRes.summary = resources - .getStringArray(R.array.auto_response)[Config.get(key)] + .getStringArray(R.array.auto_response)[Config.suAutoReponse] Config.Key.SU_NOTIFICATION -> suNotification.summary = resources - .getStringArray(R.array.su_notification)[Config.get(key)] + .getStringArray(R.array.su_notification)[Config.suNotification] Config.Key.SU_REQUEST_TIMEOUT -> requestTimeout.summary = - getString(R.string.request_timeout_summary, Config.get(key)) - Config.Key.SU_MULTIUSER_MODE -> multiuserConfig.summary = - resources.getStringArray(R.array.multiuser_summary)[Config.get(key)] - Config.Key.SU_MNT_NS -> nsConfig.summary = resources - .getStringArray(R.array.namespace_summary)[Config.get(key)] + getString(R.string.request_timeout_summary, Config.suDefaultTimeout) } } private fun setSummary() { - setSummary(Config.Key.UPDATE_CHANNEL) 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_NOTIFICATION) setSummary(Config.Key.SU_REQUEST_TIMEOUT) - setSummary(Config.Key.SU_MULTIUSER_MODE) - setSummary(Config.Key.SU_MNT_NS) } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt index 38a7dd08d..77c735fee 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt @@ -173,7 +173,7 @@ class SuRequestViewModel( return true } - when (Config.get(Config.Key.SU_AUTO_RESPONSE)) { + when (Config.suAutoReponse) { Config.Value.SU_AUTO_DENY -> { handler?.handleAction(Policy.DENY, 0) return true @@ -190,8 +190,7 @@ class SuRequestViewModel( @SuppressLint("ClickableViewAccessibility") private fun showUI() { - val seconds = Config.get(Config.Key.SU_REQUEST_TIMEOUT).toLong() - val millis = SECONDS.toMillis(seconds) + val millis = SECONDS.toMillis(Config.suDefaultTimeout.toLong()) timer = object : CountDownTimer(millis, 1000) { override fun onTick(remains: Long) { denyText.value = "%s (%d)" diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java b/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java index 39727e52d..f984ade12 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java @@ -6,6 +6,7 @@ import com.topjohnwu.magisk.App; import com.topjohnwu.magisk.BuildConfig; import com.topjohnwu.magisk.ClassMap; import com.topjohnwu.magisk.Config; +import com.topjohnwu.magisk.Info; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.ui.SplashActivity; import com.topjohnwu.magisk.view.ProgressNotification; @@ -24,8 +25,8 @@ public class DownloadApp { } public static void restore() { - String name = Utils.fmt("MagiskManager v%s(%d)", - Config.remoteManagerVersionString, Config.remoteManagerVersionCode); + String name = Utils.INSTANCE.fmt("MagiskManager v%s(%d)", + Info.remoteManagerVersionString, Info.remoteManagerVersionCode); dlInstall(name, new RestoreManager()); } @@ -33,7 +34,7 @@ public class DownloadApp { File apk = new File(App.self.getCacheDir(), "manager.apk"); ProgressNotification progress = new ProgressNotification(name); listener.progress = progress; - Networking.get(Config.managerLink) + Networking.get(Info.managerLink) .setExecutor(App.THREAD_POOL) .setDownloadProgressListener(progress) .setErrorHandler((conn, e) -> progress.dlFail()) diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.java b/app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.java deleted file mode 100644 index 122054fa8..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.java +++ /dev/null @@ -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(); - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.kt b/app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.kt new file mode 100644 index 000000000..63741cbb3 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.kt @@ -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() + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/LocaleManager.kt b/app/src/main/java/com/topjohnwu/magisk/utils/LocaleManager.kt index 692adccbb..c1c03b6a4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/LocaleManager.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/LocaleManager.kt @@ -107,9 +107,9 @@ object LocaleManager { @JvmStatic fun setLocale(wrapper: ContextWrapper) { - val localeConfig = Config.get(Config.Key.LOCALE) + val localeConfig = Config.locale locale = when { - localeConfig.isNullOrEmpty() -> defaultLocale + localeConfig.isEmpty() -> defaultLocale else -> forLanguageTag(localeConfig) } Locale.setDefault(locale) diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java b/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java index 68756e91a..8bd790d36 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java @@ -13,7 +13,7 @@ public class Logger { } 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) { @@ -21,6 +21,6 @@ public class Logger { } public static void error(String fmt, Object... args) { - error(Utils.fmt(fmt, args)); + error(Utils.INSTANCE.fmt(fmt, args)); } } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java index 13e6f65bf..042444a16 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java @@ -3,6 +3,8 @@ package com.topjohnwu.magisk.utils; import android.content.ComponentName; import android.widget.Toast; +import androidx.core.app.NotificationCompat; + import com.topjohnwu.magisk.App; import com.topjohnwu.magisk.BuildConfig; import com.topjohnwu.magisk.ClassMap; @@ -27,8 +29,6 @@ import java.util.ArrayList; import java.util.List; import java.util.jar.JarEntry; -import androidx.core.app.NotificationCompat; - public class PatchAPK { public static final String LOWERALPHA = "abcdefghijklmnopqrstuvwxyz"; @@ -110,7 +110,7 @@ public class PatchAPK { if (!Shell.su("pm install " + repack).exec().isSuccess()) return false; - Config.set(Config.Key.SU_MANAGER, pkg); + Config.setSuManager(pkg); Config.export(); RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID, 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.mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build()); 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); }); } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt b/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt index 5a1716931..fe8d0c9e8 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt @@ -4,8 +4,8 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.net.Uri -import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Const +import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.ShellUtils @@ -132,7 +132,7 @@ class RootUtils : Shell.Initializer() { job.add(context.rawResource(R.raw.util_functions)) .add(context.rawResource(R.raw.utils)) Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk") - Config.loadMagiskInfo() + Info.loadMagiskInfo() } else { job.add(context.rawResource(R.raw.nonroot_utils)) } @@ -143,9 +143,9 @@ class RootUtils : Shell.Initializer() { "export BOOTMODE=true") .exec() - Config.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean() - Config.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean() - Config.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean() + Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean() + Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean() + Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean() return true } @@ -158,7 +158,7 @@ class RootUtils : Shell.Initializer() { @JvmStatic fun reboot() { - Shell.su("/system/bin/reboot ${if (Config.recovery) "recovery" else ""}").submit() + Shell.su("/system/bin/reboot ${if (Info.recovery) "recovery" else ""}").submit() } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt b/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt index a7374bbe4..71fd12c79 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt @@ -1,10 +1,10 @@ package com.topjohnwu.magisk.utils +import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Process import android.widget.Toast -import com.topjohnwu.magisk.App import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.repository.AppRepository @@ -17,6 +17,8 @@ import java.util.* object SuLogger { + private val context: Context by inject() + @JvmStatic fun handleLogs(intent: Intent) { @@ -66,9 +68,9 @@ object SuLogger { } private fun handleNotify(policy: MagiskPolicy) { - if (policy.notification && Config.get(Config.Key.SU_NOTIFICATION) as Int == Config.Value.NOTIFICATION_TOAST) { + if (policy.notification && Config.suNotification == Config.Value.NOTIFICATION_TOAST) { Utils.toast( - App.self.getString( + context.getString( if (policy.policy == Policy.ALLOW) R.string.su_allow_toast else diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java deleted file mode 100644 index 0a513e1df..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java +++ /dev/null @@ -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 loadModulesLeanback() { - final Map 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); - } - } - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt new file mode 100644 index 000000000..92616041f --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt @@ -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().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 { + val moduleMap = ValueSortedMap() + 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) + } + } + +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XNetwork.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XNetwork.kt index f6bbede0b..7548c2274 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/XNetwork.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/XNetwork.kt @@ -1,12 +1,6 @@ package com.topjohnwu.magisk.utils 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 java.io.File @@ -19,36 +13,3 @@ fun ResponseBody.writeToFile(context: Context, fileName: String): File { } 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) -} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.java b/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.java index 9e4017424..8a7b4f244 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.java @@ -5,6 +5,8 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; +import androidx.appcompat.app.AlertDialog; + import com.topjohnwu.magisk.R; import com.topjohnwu.net.Networking; import com.topjohnwu.net.ResponseListener; @@ -12,7 +14,6 @@ import com.topjohnwu.net.ResponseListener; import java.io.InputStream; import java.util.Scanner; -import androidx.appcompat.app.AlertDialog; import ru.noties.markwon.Markwon; import ru.noties.markwon.html.HtmlPlugin; import ru.noties.markwon.image.ImagesPlugin; diff --git a/app/src/main/java/com/topjohnwu/magisk/view/Notifications.java b/app/src/main/java/com/topjohnwu/magisk/view/Notifications.java index 244c51b25..3b01372bb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/Notifications.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/Notifications.java @@ -7,19 +7,19 @@ import android.content.Context; import android.content.Intent; 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.ClassMap; -import com.topjohnwu.magisk.Config; import com.topjohnwu.magisk.Const; +import com.topjohnwu.magisk.Info; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.model.receiver.GeneralReceiver; import com.topjohnwu.magisk.ui.SplashActivity; 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 static NotificationManagerCompat mgr = NotificationManagerCompat.from(App.self); @@ -62,12 +62,12 @@ public class Notifications { public static void managerUpdate() { App app = App.self; - String name = Utils.fmt("MagiskManager v%s(%d)", - Config.remoteManagerVersionString, Config.remoteManagerVersionCode); + String name = Utils.INSTANCE.fmt("MagiskManager v%s(%d)", + Info.remoteManagerVersionString, Info.remoteManagerVersionCode); Intent intent = new Intent(app, ClassMap.get(GeneralReceiver.class)); 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); PendingIntent pendingIntent = PendingIntent.getBroadcast(app, Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT); diff --git a/app/src/main/java/com/topjohnwu/magisk/view/ProgressNotification.java b/app/src/main/java/com/topjohnwu/magisk/view/ProgressNotification.java index a7ff7721d..282b66b97 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/ProgressNotification.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/ProgressNotification.java @@ -5,13 +5,13 @@ import android.app.PendingIntent; import android.content.Intent; import android.widget.Toast; +import androidx.core.app.NotificationCompat; + import com.topjohnwu.magisk.App; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.net.DownloadProgressListener; -import androidx.core.app.NotificationCompat; - public class ProgressNotification implements DownloadProgressListener { private NotificationCompat.Builder builder; @@ -22,7 +22,7 @@ public class ProgressNotification implements DownloadProgressListener { builder = Notifications.progress(title); prevTime = System.currentTimeMillis(); 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 diff --git a/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.java b/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.java deleted file mode 100644 index ba841564c..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.java +++ /dev/null @@ -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 getShortCuts(Context context) { - ArrayList 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; - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt b/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt new file mode 100644 index 000000000..a97720bb8 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt @@ -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 { + val shortCuts = mutableListOf() + 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 + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java index 95e1364b6..de4931acf 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java @@ -4,6 +4,8 @@ import android.app.Activity; import android.app.ProgressDialog; import android.widget.Toast; +import androidx.annotation.NonNull; + import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.tasks.MagiskInstaller; 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.io.SuFile; -import androidx.annotation.NonNull; - public class EnvFixDialog extends CustomAlertDialog { public EnvFixDialog(@NonNull Activity activity) { @@ -36,7 +36,7 @@ public class EnvFixDialog extends CustomAlertDialog { @Override protected void onResult(boolean success) { 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) UiThreadHandler.handler.postDelayed(RootUtils::reboot, 5000); } diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.java b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.java index bd0fd0ad6..e429b80bf 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.java @@ -11,14 +11,14 @@ import android.os.Build; import android.view.Gravity; 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.Nullable; 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) public class FingerprintAuthDialog extends CustomAlertDialog { @@ -31,13 +31,13 @@ public class FingerprintAuthDialog extends CustomAlertDialog { super(activity); callback = onSuccess; 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(); TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint}); fingerprint.setTint(ta.getColor(0, Color.GRAY)); ta.recycle(); 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); setMessage(R.string.auth_fingerprint); setNegativeButton(R.string.close, (d, w) -> { @@ -54,7 +54,9 @@ public class FingerprintAuthDialog extends CustomAlertDialog { }); try { helper = new DialogFingerprintHelper(); - } catch (Exception ignored) {} + } catch (Exception ignored) { + ignored.printStackTrace(); + } } public FingerprintAuthDialog(@NonNull Activity activity, @NonNull Runnable onSuccess, @NonNull Runnable onFailure) { @@ -67,7 +69,7 @@ public class FingerprintAuthDialog extends CustomAlertDialog { create(); if (helper == null) { dialog.dismiss(); - Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT); + Utils.INSTANCE.toast(R.string.auth_fail, Toast.LENGTH_SHORT); } else { helper.authenticate(); dialog.show(); diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.java b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.java index 4faea6bd9..d88cf15cb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.java @@ -4,10 +4,12 @@ import android.app.Activity; import android.content.Intent; import android.widget.Toast; +import androidx.appcompat.app.AlertDialog; + import com.google.android.material.snackbar.Snackbar; import com.topjohnwu.magisk.ClassMap; -import com.topjohnwu.magisk.Config; import com.topjohnwu.magisk.Const; +import com.topjohnwu.magisk.Info; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.ui.base.IBaseLeanback; import com.topjohnwu.magisk.ui.flash.FlashActivity; @@ -19,8 +21,6 @@ import com.topjohnwu.net.Networking; import java.io.File; import java.util.List; -import androidx.appcompat.app.AlertDialog; - class InstallMethodDialog extends AlertDialog.Builder { InstallMethodDialog(Ctxt activity, List options) { @@ -50,7 +50,7 @@ class InstallMethodDialog extends AlertDialog.Builder { private void patchBoot(Ctxt activity) { 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) .setType("*/*") .addCategory(Intent.CATEGORY_OPENABLE); @@ -68,11 +68,11 @@ class InstallMethodDialog extends AlertDialog.Builder { private void downloadOnly(Ctxt activity) { activity.runWithExternalRW(() -> { - String filename = Utils.fmt("Magisk-v%s(%d).zip", - Config.remoteMagiskVersionString, Config.remoteMagiskVersionCode); + String filename = Utils.INSTANCE.fmt("Magisk-v%s(%d).zip", + Info.remoteMagiskVersionString, Info.remoteMagiskVersionCode); File zip = new File(Const.EXTERNAL_PATH, filename); ProgressNotification progress = new ProgressNotification(filename); - Networking.get(Config.magiskLink) + Networking.get(Info.magiskLink) .setDownloadProgressListener(progress) .setErrorHandler((conn, e) -> progress.dlFail()) .getAsFile(zip, f -> { diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.java b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.java index e1198a92f..6426162b3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.java @@ -4,7 +4,7 @@ import android.app.Activity; import android.net.Uri; import android.text.TextUtils; -import com.topjohnwu.magisk.Config; +import com.topjohnwu.magisk.Info; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.ui.base.IBaseLeanback; import com.topjohnwu.magisk.utils.Utils; @@ -18,8 +18,8 @@ import java.util.List; public class MagiskInstallDialog extends CustomAlertDialog { public MagiskInstallDialog(Ctxt a) { super(a); - String filename = Utils.fmt("Magisk-v%s(%d).zip", - Config.remoteMagiskVersionString, Config.remoteMagiskVersionCode); + String filename = Utils.INSTANCE.fmt("Magisk-v%s(%d).zip", + Info.remoteMagiskVersionString, Info.remoteMagiskVersionCode); setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.magisk))); setMessage(a.getString(R.string.repo_install_msg, filename)); setCancelable(true); @@ -36,13 +36,13 @@ public class MagiskInstallDialog extends CustomAlertDialog { } new InstallMethodDialog(a, options).show(); }); - if (!TextUtils.isEmpty(Config.magiskNoteLink)) { + if (!TextUtils.isEmpty(Info.magiskNoteLink)) { 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 - Utils.openLink(a, Uri.parse(Config.magiskNoteLink)); + Utils.INSTANCE.openLink(a, Uri.parse(Info.magiskNoteLink)); } else { - MarkDownWindow.show(a, null, Config.magiskNoteLink); + MarkDownWindow.show(a, null, Info.magiskNoteLink); } }); } diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/ManagerInstallDialog.java b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/ManagerInstallDialog.java index 4863f0daa..80828bfd4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/ManagerInstallDialog.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/ManagerInstallDialog.java @@ -5,7 +5,7 @@ import android.text.TextUtils; import androidx.annotation.NonNull; -import com.topjohnwu.magisk.Config; +import com.topjohnwu.magisk.Info; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.utils.DownloadApp; import com.topjohnwu.magisk.utils.Utils; @@ -15,14 +15,14 @@ public class ManagerInstallDialog extends CustomAlertDialog { public ManagerInstallDialog(@NonNull Activity a) { super(a); - String name = Utils.fmt("MagiskManager v%s(%d)", - Config.remoteManagerVersionString, Config.remoteManagerVersionCode); + String name = Utils.INSTANCE.fmt("MagiskManager v%s(%d)", + Info.remoteManagerVersionString, Info.remoteManagerVersionCode); setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.app_name))); setMessage(a.getString(R.string.repo_install_msg, name)); setCancelable(true); setPositiveButton(R.string.install, (d, i) -> DownloadApp.upgrade(name)); - if (!TextUtils.isEmpty(Config.managerNoteLink)) { - setNeutralButton(R.string.app_changelog, (d, i) -> MarkDownWindow.show(a, null, Config.managerNoteLink)); + if (!TextUtils.isEmpty(Info.managerNoteLink)) { + setNeutralButton(R.string.app_changelog, (d, i) -> MarkDownWindow.show(a, null, Info.managerNoteLink)); } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/UninstallDialog.java b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/UninstallDialog.java index 4fae38407..53892f0d5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/UninstallDialog.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/UninstallDialog.java @@ -7,9 +7,11 @@ import android.net.Uri; import android.text.TextUtils; import android.widget.Toast; +import androidx.annotation.NonNull; + import com.topjohnwu.magisk.ClassMap; -import com.topjohnwu.magisk.Config; import com.topjohnwu.magisk.Const; +import com.topjohnwu.magisk.Info; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.ui.flash.FlashActivity; import com.topjohnwu.magisk.utils.Utils; @@ -19,8 +21,6 @@ import com.topjohnwu.superuser.Shell; import java.io.File; -import androidx.annotation.NonNull; - public class UninstallDialog extends CustomAlertDialog { public UninstallDialog(@NonNull Activity activity) { @@ -34,17 +34,17 @@ public class UninstallDialog extends CustomAlertDialog { Shell.su("restore_imgs").submit(result -> { dialog.cancel(); if (result.isSuccess()) { - Utils.toast(R.string.restore_done, Toast.LENGTH_SHORT); + Utils.INSTANCE.toast(R.string.restore_done, Toast.LENGTH_SHORT); } 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) -> { File zip = new File(activity.getFilesDir(), "uninstaller.zip"); ProgressNotification progress = new ProgressNotification(zip.getName()); - Networking.get(Config.uninstallerLink) + Networking.get(Info.uninstallerLink) .setDownloadProgressListener(progress) .setErrorHandler(((conn, e) -> progress.dlFail())) .getAsFile(zip, f -> { diff --git a/app/src/main/res/xml/app_settings.xml b/app/src/main/res/xml/app_settings.xml index b7a6a0f4b..dead7c116 100644 --- a/app/src/main/res/xml/app_settings.xml +++ b/app/src/main/res/xml/app_settings.xml @@ -6,6 +6,7 @@ @@ -95,28 +96,33 @@