You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Added (ported back) features from initial design [retrofit,moshi,kotpref]
Marked most of the old classes using Networking as deprecated to clearly visualise their future removal
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package com.topjohnwu.magisk.data.database
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.topjohnwu.magisk.model.entity.Repository
|
||||
|
||||
@Database(
|
||||
version = 1,
|
||||
entities = [Repository::class]
|
||||
)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val NAME = "database"
|
||||
}
|
||||
|
||||
abstract fun repoDao(): RepositoryDao
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.topjohnwu.magisk.data.database
|
||||
|
||||
import com.topjohnwu.magisk.Config
|
||||
import com.topjohnwu.magisk.data.database.base.*
|
||||
import com.topjohnwu.magisk.model.entity.MagiskLog
|
||||
import com.topjohnwu.magisk.model.entity.toLog
|
||||
import com.topjohnwu.magisk.model.entity.toMap
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class LogDao : BaseDao() {
|
||||
|
||||
override val table = DatabaseDefinition.Table.LOG
|
||||
|
||||
fun deleteOutdated(
|
||||
suTimeout: Long = Config.suLogTimeout * TimeUnit.DAYS.toMillis(1)
|
||||
) = query<Delete> {
|
||||
condition {
|
||||
lessThan("time", suTimeout.toString())
|
||||
}
|
||||
}.ignoreElement()
|
||||
|
||||
fun deleteAll() = query<Delete> {}.ignoreElement()
|
||||
|
||||
fun fetchAll() = query<Select> {
|
||||
orderBy("time", Order.DESC)
|
||||
}.flattenAsFlowable { it }
|
||||
.map { it.toLog() }
|
||||
.toList()
|
||||
|
||||
fun put(log: MagiskLog) = query<Insert> {
|
||||
values(log.toMap())
|
||||
}.ignoreElement()
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Deprecated
|
||||
public class MagiskDB {
|
||||
|
||||
private static final String POLICY_TABLE = "policies";
|
||||
@@ -27,20 +28,24 @@ public class MagiskDB {
|
||||
private static final String SETTINGS_TABLE = "settings";
|
||||
private static final String STRINGS_TABLE = "strings";
|
||||
|
||||
private PackageManager pm;
|
||||
private final PackageManager pm;
|
||||
|
||||
@Deprecated
|
||||
public MagiskDB(Context context) {
|
||||
pm = context.getPackageManager();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void deletePolicy(Policy policy) {
|
||||
deletePolicy(policy.uid);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private List<String> rawSQL(String fmt, Object... args) {
|
||||
return Shell.su("magisk --sqlite '" + Utils.fmt(fmt, args) + "'").exec().getOut();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private List<ContentValues> SQL(String fmt, Object... args) {
|
||||
List<ContentValues> list = new ArrayList<>();
|
||||
for (String raw : rawSQL(fmt, args)) {
|
||||
@@ -57,6 +62,7 @@ public class MagiskDB {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private String toSQL(ContentValues values) {
|
||||
StringBuilder keys = new StringBuilder(), vals = new StringBuilder();
|
||||
keys.append('(');
|
||||
@@ -80,6 +86,7 @@ public class MagiskDB {
|
||||
return keys.toString();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void clearOutdated() {
|
||||
rawSQL(
|
||||
"DELETE FROM %s WHERE until > 0 AND until < %d;" +
|
||||
@@ -89,14 +96,17 @@ public class MagiskDB {
|
||||
);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void deletePolicy(String pkg) {
|
||||
rawSQL("DELETE FROM %s WHERE package_name=\"%s\"", POLICY_TABLE, pkg);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void deletePolicy(int uid) {
|
||||
rawSQL("DELETE FROM %s WHERE uid=%d", POLICY_TABLE, uid);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Policy getPolicy(int uid) {
|
||||
List<ContentValues> res =
|
||||
SQL("SELECT * FROM %s WHERE uid=%d", POLICY_TABLE, uid);
|
||||
@@ -110,10 +120,12 @@ public class MagiskDB {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void updatePolicy(Policy policy) {
|
||||
rawSQL("REPLACE INTO %s %s", POLICY_TABLE, toSQL(policy.getContentValues()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public List<Policy> getPolicyList() {
|
||||
List<Policy> list = new ArrayList<>();
|
||||
for (ContentValues values : SQL("SELECT * FROM %s WHERE uid/100000=%d", POLICY_TABLE, Const.USER_ID)) {
|
||||
@@ -127,6 +139,7 @@ public class MagiskDB {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public List<List<SuLogEntry>> getLogs() {
|
||||
List<List<SuLogEntry>> ret = new ArrayList<>();
|
||||
List<SuLogEntry> list = null;
|
||||
@@ -144,18 +157,22 @@ public class MagiskDB {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void addLog(SuLogEntry log) {
|
||||
rawSQL("INSERT INTO %s %s", LOG_TABLE, toSQL(log.getContentValues()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void clearLogs() {
|
||||
rawSQL("DELETE FROM %s", LOG_TABLE);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void rmSettings(String key) {
|
||||
rawSQL("DELETE FROM %s WHERE key=\"%s\"", SETTINGS_TABLE, key);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setSettings(String key, int value) {
|
||||
ContentValues data = new ContentValues();
|
||||
data.put("key", key);
|
||||
@@ -163,6 +180,7 @@ public class MagiskDB {
|
||||
rawSQL("REPLACE INTO %s %s", SETTINGS_TABLE, toSQL(data));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getSettings(String key, int defaultValue) {
|
||||
List<ContentValues> res = SQL("SELECT value FROM %s WHERE key=\"%s\"", SETTINGS_TABLE, key);
|
||||
if (res.isEmpty())
|
||||
@@ -170,6 +188,7 @@ public class MagiskDB {
|
||||
return res.get(0).getAsInteger("value");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setStrings(String key, String value) {
|
||||
if (value == null) {
|
||||
rawSQL("DELETE FROM %s WHERE key=\"%s\"", STRINGS_TABLE, key);
|
||||
@@ -181,6 +200,7 @@ public class MagiskDB {
|
||||
rawSQL("REPLACE INTO %s %s", STRINGS_TABLE, toSQL(data));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String getStrings(String key, String defaultValue) {
|
||||
List<ContentValues> res = SQL("SELECT value FROM %s WHERE key=\"%s\"", STRINGS_TABLE, key);
|
||||
if (res.isEmpty())
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.topjohnwu.magisk.data.database
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import com.topjohnwu.magisk.Constants
|
||||
import com.topjohnwu.magisk.data.database.base.*
|
||||
import com.topjohnwu.magisk.model.entity.MagiskPolicy
|
||||
import com.topjohnwu.magisk.model.entity.toMap
|
||||
import com.topjohnwu.magisk.model.entity.toPolicy
|
||||
import com.topjohnwu.magisk.utils.now
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
class PolicyDao(
|
||||
private val context: Context
|
||||
) : BaseDao() {
|
||||
|
||||
override val table: String = DatabaseDefinition.Table.POLICY
|
||||
|
||||
fun deleteOutdated(
|
||||
nowSeconds: Long = TimeUnit.MILLISECONDS.toSeconds(now)
|
||||
) = query<Delete> {
|
||||
condition {
|
||||
greaterThan("until", "0")
|
||||
and {
|
||||
lessThan("until", nowSeconds.toString())
|
||||
}
|
||||
}
|
||||
}.ignoreElement()
|
||||
|
||||
fun delete(packageName: String) = query<Delete> {
|
||||
condition {
|
||||
equals("package_name", packageName)
|
||||
}
|
||||
}.ignoreElement()
|
||||
|
||||
fun delete(uid: Int) = query<Delete> {
|
||||
condition {
|
||||
equals("uid", uid.toString())
|
||||
}
|
||||
}.ignoreElement()
|
||||
|
||||
fun fetch(uid: Int) = query<Select> {
|
||||
condition {
|
||||
equals("uid", uid.toString())
|
||||
}
|
||||
}.map { it.first().toPolicy(context.packageManager) }
|
||||
.doOnError {
|
||||
if (it is PackageManager.NameNotFoundException) {
|
||||
delete(uid).subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
fun update(policy: MagiskPolicy) = query<Replace> {
|
||||
|
||||
values(policy.toMap())
|
||||
}.ignoreElement()
|
||||
|
||||
fun fetchAll() = query<Select> {
|
||||
condition {
|
||||
equals("uid/100000", Constants.USER_ID.toString())
|
||||
}
|
||||
}.flattenAsFlowable { it }
|
||||
.map { it.toPolicy(context.packageManager) }
|
||||
.toList()
|
||||
|
||||
}
|
||||
@@ -11,13 +11,15 @@ 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 SQLiteDatabase mDb;
|
||||
private final SQLiteDatabase mDb;
|
||||
|
||||
@Deprecated
|
||||
public RepoDatabaseHelper(Context context) {
|
||||
super(context, "repo.db", null, DATABASE_VER);
|
||||
mDb = getWritableDatabase();
|
||||
@@ -46,19 +48,23 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
||||
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;
|
||||
@@ -66,10 +72,12 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
@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()) {
|
||||
@@ -79,10 +87,12 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
||||
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)) {
|
||||
@@ -95,6 +105,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
||||
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)) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.topjohnwu.magisk.data.database
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.skoumal.teanity.database.BaseDao
|
||||
import com.topjohnwu.magisk.model.entity.Repository
|
||||
|
||||
@Dao
|
||||
interface RepositoryDao : BaseDao<Repository> {
|
||||
|
||||
@Query("DELETE FROM repos")
|
||||
override fun deleteAll()
|
||||
|
||||
@Query("SELECT * FROM repos")
|
||||
override fun fetchAll(): List<Repository>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.topjohnwu.magisk.data.database
|
||||
|
||||
import com.topjohnwu.magisk.data.database.base.*
|
||||
|
||||
class SettingsDao : BaseDao() {
|
||||
|
||||
override val table = DatabaseDefinition.Table.SETTINGS
|
||||
|
||||
fun delete(key: String) = query<Delete> {
|
||||
condition { equals("key", key) }
|
||||
}.ignoreElement()
|
||||
|
||||
fun put(key: String, value: Int) = query<Insert> {
|
||||
values(key to value.toString())
|
||||
}.ignoreElement()
|
||||
|
||||
fun fetch(key: String) = query<Select> {
|
||||
condition { equals("key", key) }
|
||||
}.map { it.first().values.first().toIntOrNull() ?: -1 }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.topjohnwu.magisk.data.database
|
||||
|
||||
import com.topjohnwu.magisk.data.database.base.*
|
||||
|
||||
class StringsDao : BaseDao() {
|
||||
|
||||
override val table = DatabaseDefinition.Table.STRINGS
|
||||
|
||||
fun delete(key: String) = query<Delete> {
|
||||
condition { equals("key", key) }
|
||||
}.ignoreElement()
|
||||
|
||||
fun put(key: String, value: String) = query<Insert> {
|
||||
values(key to value)
|
||||
}.ignoreElement()
|
||||
|
||||
fun fetch(key: String, default: String = "") = query<Select> {
|
||||
fields("value")
|
||||
condition { equals("key", key) }
|
||||
}.map { it.firstOrNull()?.values?.firstOrNull() ?: default }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.topjohnwu.magisk.data.database.base
|
||||
|
||||
abstract class BaseDao {
|
||||
|
||||
abstract val table: String
|
||||
|
||||
inline fun <reified Builder : MagiskQueryBuilder> query(builder: Builder.() -> Unit) =
|
||||
Builder::class.java.newInstance()
|
||||
.apply { table = this@BaseDao.table }
|
||||
.apply(builder)
|
||||
.toString()
|
||||
.let { MagiskQuery(it) }
|
||||
.query()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.topjohnwu.magisk.data.database.base
|
||||
|
||||
import androidx.annotation.AnyThread
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import io.reactivex.Single
|
||||
|
||||
object DatabaseDefinition {
|
||||
|
||||
object Table {
|
||||
const val POLICY = "policies"
|
||||
const val LOG = "logs"
|
||||
const val SETTINGS = "settings"
|
||||
const val STRINGS = "strings"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
fun MagiskQuery.query() = query.su()
|
||||
|
||||
fun String.suRaw() = Single.just(Shell.su(this))
|
||||
.map { it.exec().out }
|
||||
|
||||
fun String.su() = suRaw()
|
||||
.map { it.toMap() }
|
||||
|
||||
fun List<String>.toMap() = map { it.split(Regex("\\|")) }
|
||||
.map { it.toMapInternal() }
|
||||
|
||||
private fun List<String>.toMapInternal() = map { it.split("=", limit = 2) }
|
||||
.filter { it.size == 2 }
|
||||
.map { Pair(it[0], it[1]) }
|
||||
.toMap()
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.topjohnwu.magisk.data.database.base
|
||||
|
||||
data class MagiskQuery(private val _query: String) {
|
||||
val query = "magisk --sqlite $_query"
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.topjohnwu.magisk.data.database.base
|
||||
|
||||
import androidx.annotation.StringDef
|
||||
import com.topjohnwu.magisk.data.database.base.Order.Companion.ASC
|
||||
import com.topjohnwu.magisk.data.database.base.Order.Companion.DESC
|
||||
|
||||
interface MagiskQueryBuilder {
|
||||
|
||||
val requestType: String
|
||||
var table: String
|
||||
|
||||
companion object {
|
||||
inline operator fun <reified Builder : MagiskQueryBuilder> invoke(builder: Builder.() -> Unit): MagiskQuery =
|
||||
Builder::class.java.newInstance()
|
||||
.apply(builder)
|
||||
.toString()
|
||||
.let { MagiskQuery(it) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Delete : MagiskQueryBuilder {
|
||||
override val requestType: String = "DELETE FROM"
|
||||
override var table = ""
|
||||
|
||||
private var condition = ""
|
||||
|
||||
fun condition(builder: Condition.() -> Unit) {
|
||||
condition = Condition().apply(builder).toString()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return StringBuilder()
|
||||
.appendln(requestType)
|
||||
.appendln(table)
|
||||
.appendln(condition)
|
||||
.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class Select : MagiskQueryBuilder {
|
||||
override val requestType: String get() = "SELECT $fields FROM"
|
||||
override lateinit var table: String
|
||||
|
||||
private var fields = "*"
|
||||
private var condition = ""
|
||||
private var orderField = ""
|
||||
|
||||
fun fields(vararg newFields: String) {
|
||||
if (newFields.isEmpty()) {
|
||||
fields = "*"
|
||||
return
|
||||
}
|
||||
fields = newFields.joinToString(", ")
|
||||
}
|
||||
|
||||
fun condition(builder: Condition.() -> Unit) {
|
||||
condition = Condition().apply(builder).toString()
|
||||
}
|
||||
|
||||
fun orderBy(field: String, @OrderStrict order: String) {
|
||||
orderField = "ORDER BY $field $order"
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return StringBuilder()
|
||||
.appendln(requestType)
|
||||
.appendln(table)
|
||||
.appendln(condition)
|
||||
.appendln(orderField)
|
||||
.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class Replace : Insert() {
|
||||
override val requestType: String = "REPLACE INTO"
|
||||
}
|
||||
|
||||
open class Insert : MagiskQueryBuilder {
|
||||
override val requestType: String = "INSERT INTO"
|
||||
override lateinit var table: String
|
||||
|
||||
private val keys get() = _values.keys.joinToString(",")
|
||||
private val values get() = _values.values.joinToString(",")
|
||||
private var _values: Map<String, String> = mapOf()
|
||||
|
||||
fun values(vararg pairs: Pair<String, String>) {
|
||||
_values = pairs.toMap()
|
||||
}
|
||||
|
||||
fun values(values: Map<String, String>) {
|
||||
_values = values
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return StringBuilder()
|
||||
.appendln(requestType)
|
||||
.appendln(table)
|
||||
.appendln("($keys) VALUES($values)")
|
||||
.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class Condition {
|
||||
|
||||
private val conditionWord = "WHERE %s"
|
||||
private var condition: String = ""
|
||||
|
||||
fun equals(field: String, value: String) {
|
||||
condition = "$field=\"$value\""
|
||||
}
|
||||
|
||||
fun greaterThan(field: String, value: String) {
|
||||
condition = "$field > $value"
|
||||
}
|
||||
|
||||
fun lessThan(field: String, value: String) {
|
||||
condition = "$field < $value"
|
||||
}
|
||||
|
||||
fun greaterOrEqualTo(field: String, value: String) {
|
||||
condition = "$field >= $value"
|
||||
}
|
||||
|
||||
fun lessOrEqualTo(field: String, value: String) {
|
||||
condition = "$field <= $value"
|
||||
}
|
||||
|
||||
fun and(builder: Condition.() -> Unit) {
|
||||
condition += " " + Condition().apply(builder).condition
|
||||
}
|
||||
|
||||
fun or(builder: Condition.() -> Unit) {
|
||||
condition += " " + Condition().apply(builder).condition
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return conditionWord.format(condition)
|
||||
}
|
||||
}
|
||||
|
||||
class Order {
|
||||
|
||||
@set:OrderStrict
|
||||
var order = DESC
|
||||
var field = ""
|
||||
|
||||
companion object {
|
||||
const val ASC = "ASC"
|
||||
const val DESC = "DESC"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@StringDef(ASC, DESC)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class OrderStrict
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.topjohnwu.magisk.data.network
|
||||
|
||||
import com.topjohnwu.magisk.model.entity.GithubRepo
|
||||
import io.reactivex.Single
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
||||
interface GithubApiServices {
|
||||
|
||||
@GET("users/Magisk-Modules-Repo/repos")
|
||||
fun fetchRepos(
|
||||
@Query("page") page: Int,
|
||||
@Query("per_page") count: Int = REPOS_PER_PAGE,
|
||||
@Query("sort") sortOrder: String = "pushed"
|
||||
): Single<List<GithubRepo>>
|
||||
|
||||
companion object {
|
||||
const val REPOS_PER_PAGE = 100
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.topjohnwu.magisk.data.network
|
||||
|
||||
import com.topjohnwu.magisk.Constants
|
||||
import com.topjohnwu.magisk.model.entity.MagiskConfig
|
||||
import io.reactivex.Single
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Streaming
|
||||
import retrofit2.http.Url
|
||||
|
||||
|
||||
interface GithubRawApiServices {
|
||||
|
||||
//region topjohnwu/magisk_files
|
||||
|
||||
@GET("$MAGISK_FILES/master/stable.json")
|
||||
fun fetchConfig(): Single<MagiskConfig>
|
||||
|
||||
@GET("$MAGISK_FILES/master/beta.json")
|
||||
fun fetchBetaConfig(): Single<MagiskConfig>
|
||||
|
||||
@GET("$MAGISK_FILES/master/canary_builds/release.json")
|
||||
fun fetchCanaryConfig(): Single<MagiskConfig>
|
||||
|
||||
@GET("$MAGISK_FILES/master/canary_builds/canary.json")
|
||||
fun fetchCanaryDebugConfig(): Single<MagiskConfig>
|
||||
|
||||
@GET("$MAGISK_FILES/{$REVISION}/snet.apk")
|
||||
@Streaming
|
||||
fun fetchSafetynet(@Path(REVISION) revision: String = Constants.SNET_REVISION): Single<ResponseBody>
|
||||
|
||||
@GET("$MAGISK_FILES/{$REVISION}/bootctl")
|
||||
@Streaming
|
||||
fun fetchBootctl(@Path(REVISION) revision: String = Constants.BOOTCTL_REVISION): Single<ResponseBody>
|
||||
|
||||
//endregion
|
||||
|
||||
//region topjohnwu/Magisk/master
|
||||
|
||||
@GET("$MAGISK_MASTER/scripts/module_installer.sh")
|
||||
@Streaming
|
||||
fun fetchModuleInstaller(): Single<ResponseBody>
|
||||
|
||||
//endregion
|
||||
|
||||
//region Magisk-Modules-Repo
|
||||
|
||||
@GET("$MAGISK_MODULES/{$MODULE}/master/{$FILE}")
|
||||
@Streaming
|
||||
fun fetchFile(id: String, file: String): Single<ResponseBody>
|
||||
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* This method shall be used exclusively for fetching files from urls from previous requests.
|
||||
* Him, who uses it in a wrong way, shall die in an eternal flame.
|
||||
* */
|
||||
@GET
|
||||
@Streaming
|
||||
fun fetchFile(@Url url: String): Single<ResponseBody>
|
||||
|
||||
|
||||
companion object {
|
||||
private const val REVISION = "revision"
|
||||
private const val MODULE = "module"
|
||||
private const val FILE = "file"
|
||||
|
||||
|
||||
private const val MAGISK_FILES = "topjohnwu/magisk_files"
|
||||
private const val MAGISK_MASTER = "topjohnwu/Magisk/master"
|
||||
private const val MAGISK_MODULES = "Magisk-Modules-Repo"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.topjohnwu.magisk.data.network
|
||||
|
||||
import io.reactivex.Single
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Streaming
|
||||
|
||||
|
||||
interface GithubServices {
|
||||
|
||||
@GET("Magisk-Modules-Repo/{$MODULE}/archive/master.zip")
|
||||
@Streaming
|
||||
fun fetchModuleZip(@Path(MODULE) module: String): Single<ResponseBody>
|
||||
|
||||
|
||||
companion object {
|
||||
private const val MODULE = "module"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.topjohnwu.magisk.data.repository
|
||||
|
||||
import com.topjohnwu.magisk.Constants
|
||||
import com.topjohnwu.magisk.data.database.LogDao
|
||||
import com.topjohnwu.magisk.data.database.base.suRaw
|
||||
import com.topjohnwu.magisk.model.entity.MagiskLog
|
||||
import com.topjohnwu.magisk.model.entity.WrappedMagiskLog
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
class LogRepository(
|
||||
private val logDao: LogDao
|
||||
) {
|
||||
|
||||
fun fetchLogs() = logDao.fetchAll()
|
||||
.map { it.sortByDescending { it.date.time }; it }
|
||||
.map { it.wrap() }
|
||||
|
||||
fun fetchMagiskLogs() = "tail -n 5000 ${Constants.MAGISK_LOG}".suRaw()
|
||||
.filter { it.isNotEmpty() }
|
||||
.map { Timber.i(it.toString()); it }
|
||||
|
||||
private fun List<MagiskLog>.wrap(): List<WrappedMagiskLog> {
|
||||
val day = TimeUnit.DAYS.toMillis(1)
|
||||
var currentDay = firstOrNull()?.date?.time ?: return listOf()
|
||||
var tempList = this
|
||||
val outList = mutableListOf<WrappedMagiskLog>()
|
||||
|
||||
while (tempList.isNotEmpty()) {
|
||||
val logsGivenDay = takeWhile { it.date.time / day == currentDay / day }
|
||||
currentDay = tempList.firstOrNull()?.date?.time ?: currentDay + day
|
||||
|
||||
if (logsGivenDay.isEmpty())
|
||||
continue
|
||||
|
||||
outList.add(WrappedMagiskLog(currentDay / day * day, logsGivenDay))
|
||||
tempList = tempList.subList(logsGivenDay.size, tempList.size)
|
||||
}
|
||||
|
||||
return outList
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.topjohnwu.magisk.data.repository
|
||||
|
||||
import android.content.Context
|
||||
import com.topjohnwu.magisk.KConfig
|
||||
import com.topjohnwu.magisk.data.database.base.suRaw
|
||||
import com.topjohnwu.magisk.data.network.GithubRawApiServices
|
||||
import com.topjohnwu.magisk.model.entity.Version
|
||||
import com.topjohnwu.magisk.utils.writeToFile
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.functions.BiFunction
|
||||
|
||||
class MagiskRepository(
|
||||
private val context: Context,
|
||||
private val apiRaw: GithubRawApiServices
|
||||
) {
|
||||
|
||||
private val config = apiRaw.fetchConfig()
|
||||
private val configBeta = apiRaw.fetchBetaConfig()
|
||||
private val configCanary = apiRaw.fetchCanaryConfig()
|
||||
private val configCanaryDebug = apiRaw.fetchCanaryDebugConfig()
|
||||
|
||||
|
||||
fun fetchMagisk() = fetchConfig()
|
||||
.flatMap { apiRaw.fetchFile(it.magisk.link) }
|
||||
.map { it.writeToFile(context, FILE_MAGISK_ZIP) }
|
||||
|
||||
fun fetchManager() = fetchConfig()
|
||||
.flatMap { apiRaw.fetchFile(it.app.link) }
|
||||
.map { it.writeToFile(context, FILE_MAGISK_APK) }
|
||||
|
||||
fun fetchUninstaller() = fetchConfig()
|
||||
.flatMap { apiRaw.fetchFile(it.uninstaller.link) }
|
||||
.map { it.writeToFile(context, FILE_UNINSTALLER_ZIP) }
|
||||
|
||||
fun fetchSafetynet() = apiRaw
|
||||
.fetchSafetynet()
|
||||
.map { it.writeToFile(context, FILE_SAFETY_NET_APK) }
|
||||
|
||||
fun fetchBootctl() = apiRaw
|
||||
.fetchBootctl()
|
||||
.map { it.writeToFile(context, FILE_BOOTCTL_SH) }
|
||||
|
||||
|
||||
fun fetchConfig() = when (KConfig.updateChannel) {
|
||||
KConfig.UpdateChannel.STABLE -> config
|
||||
KConfig.UpdateChannel.BETA -> configBeta
|
||||
KConfig.UpdateChannel.CANARY -> configCanary
|
||||
KConfig.UpdateChannel.CANARY_DEBUG -> configCanaryDebug
|
||||
}
|
||||
|
||||
|
||||
fun fetchMagiskVersion(): Single<Version> = Single.zip(
|
||||
fetchMagiskVersionName(),
|
||||
fetchMagiskVersionCode(),
|
||||
BiFunction { versionName, versionCode ->
|
||||
Version(versionName, versionCode)
|
||||
}
|
||||
)
|
||||
|
||||
private fun fetchMagiskVersionName() = "magisk -v".suRaw()
|
||||
.map { it.first() }
|
||||
.map { it.substring(0 until it.indexOf(":")) }
|
||||
.onErrorReturn { "Unknown" }
|
||||
|
||||
private fun fetchMagiskVersionCode() = "magisk -V".suRaw()
|
||||
.map { it.first() }
|
||||
.map { it.toIntOrNull() ?: -1 }
|
||||
.onErrorReturn { -1 }
|
||||
|
||||
companion object {
|
||||
const val FILE_MAGISK_ZIP = "magisk.zip"
|
||||
const val FILE_MAGISK_APK = "magisk.apk"
|
||||
const val FILE_UNINSTALLER_ZIP = "uninstaller.zip"
|
||||
const val FILE_SAFETY_NET_APK = "safetynet.apk"
|
||||
const val FILE_BOOTCTL_SH = "bootctl"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.topjohnwu.magisk.data.repository
|
||||
|
||||
import android.content.Context
|
||||
import com.topjohnwu.magisk.data.network.GithubApiServices
|
||||
import com.topjohnwu.magisk.data.network.GithubRawApiServices
|
||||
import com.topjohnwu.magisk.data.network.GithubServices
|
||||
import com.topjohnwu.magisk.model.entity.GithubRepo
|
||||
import com.topjohnwu.magisk.model.entity.toRepository
|
||||
import com.topjohnwu.magisk.utils.writeToFile
|
||||
import com.topjohnwu.magisk.utils.writeToString
|
||||
import io.reactivex.Single
|
||||
|
||||
class ModuleRepository(
|
||||
private val context: Context,
|
||||
private val apiRaw: GithubRawApiServices,
|
||||
private val api: GithubApiServices,
|
||||
private val apiWeb: GithubServices
|
||||
) {
|
||||
|
||||
fun fetchModules() = fetchAllRepos()
|
||||
.flattenAsFlowable { it }
|
||||
.flatMapSingle { fetchProperties(it.name, it.updatedAtMillis) }
|
||||
.toList()
|
||||
|
||||
fun fetchInstallFile(module: String) = apiRaw
|
||||
.fetchFile(module, FILE_INSTALL_SH)
|
||||
.map { it.writeToFile(context, FILE_INSTALL_SH) }
|
||||
|
||||
fun fetchReadme(module: String) = apiRaw
|
||||
.fetchFile(module, FILE_README_MD)
|
||||
.map { it.writeToString() }
|
||||
|
||||
fun fetchConfig(module: String) = apiRaw
|
||||
.fetchFile(module, FILE_CONFIG_SH)
|
||||
.map { it.writeToFile(context, FILE_CONFIG_SH) }
|
||||
|
||||
fun fetchInstallZip(module: String) = apiWeb
|
||||
.fetchModuleZip(module)
|
||||
.map { it.writeToFile(context, FILE_INSTALL_ZIP) }
|
||||
|
||||
fun fetchInstaller() = apiRaw
|
||||
.fetchModuleInstaller()
|
||||
.map { it.writeToFile(context, FILE_MODULE_INSTALLER_SH) }
|
||||
|
||||
|
||||
private fun fetchProperties(module: String, lastChanged: Long) = apiRaw
|
||||
.fetchFile(module, "module.prop")
|
||||
.map { it.toRepository(lastChanged) }
|
||||
|
||||
private fun fetchAllRepos(page: Int = 0): Single<List<GithubRepo>> = api.fetchRepos(page)
|
||||
.flatMap {
|
||||
if (it.size == GithubApiServices.REPOS_PER_PAGE) {
|
||||
fetchAllRepos(page + 1).map { newList -> it + newList }
|
||||
} else {
|
||||
Single.just(it)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val FILE_INSTALL_SH = "install.sh"
|
||||
const val FILE_README_MD = "README.md"
|
||||
const val FILE_CONFIG_SH = "config.sh"
|
||||
const val FILE_INSTALL_ZIP = "install.zip"
|
||||
const val FILE_MODULE_INSTALLER_SH = "module_installer.sh"
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user