Commit 7756e107 authored by topjohnwu's avatar topjohnwu

Rewrite configs with Kotlin delagate properties

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