Migrate Magisk Modules to Kotlin
This commit is contained in:
parent
cdaff5b39c
commit
0c17ea5755
@ -1,86 +1,111 @@
|
|||||||
package com.topjohnwu.magisk.model.entity
|
package com.topjohnwu.magisk.model.entity
|
||||||
|
|
||||||
import android.os.Parcelable
|
|
||||||
import androidx.annotation.AnyThread
|
|
||||||
import androidx.annotation.NonNull
|
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
import com.topjohnwu.magisk.Const
|
import com.topjohnwu.magisk.Const
|
||||||
import com.topjohnwu.magisk.data.database.base.su
|
import com.topjohnwu.superuser.Shell
|
||||||
import io.reactivex.Single
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
import kotlinx.android.parcel.Parcelize
|
|
||||||
import okhttp3.ResponseBody
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
interface MagiskModule : Parcelable {
|
abstract class BaseModule : Comparable<BaseModule> {
|
||||||
val id: String
|
abstract var id: String
|
||||||
val name: String
|
protected set
|
||||||
val author: String
|
abstract var name: String
|
||||||
val version: String
|
protected set
|
||||||
val versionCode: String
|
abstract var author: String
|
||||||
val description: String
|
protected set
|
||||||
|
abstract var version: String
|
||||||
|
protected set
|
||||||
|
abstract var versionCode: Int
|
||||||
|
protected set
|
||||||
|
abstract var description: String
|
||||||
|
protected set
|
||||||
|
|
||||||
|
@Throws(NumberFormatException::class)
|
||||||
|
protected fun parseProps(props: List<String>) {
|
||||||
|
for (line in props) {
|
||||||
|
val prop = line.split("=".toRegex(), 2).map { it.trim() }
|
||||||
|
if (prop.size != 2)
|
||||||
|
continue
|
||||||
|
|
||||||
|
val key = prop[0]
|
||||||
|
val value = prop[1]
|
||||||
|
if (key.isEmpty() || key[0] == '#')
|
||||||
|
continue
|
||||||
|
|
||||||
|
when (key) {
|
||||||
|
"id" -> id = value
|
||||||
|
"name" -> name = value
|
||||||
|
"version" -> version = value
|
||||||
|
"versionCode" -> versionCode = value.toInt()
|
||||||
|
"author" -> author = value
|
||||||
|
"description" -> description = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun compareTo(other: BaseModule) = name.compareTo(other.name, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity(tableName = "repos")
|
class Module(path: String) : BaseModule() {
|
||||||
@Parcelize
|
override var id: String = ""
|
||||||
data class Repository(
|
override var name: String = ""
|
||||||
@PrimaryKey @NonNull
|
override var author: String = ""
|
||||||
override val id: String,
|
override var version: String = ""
|
||||||
override val name: String,
|
override var versionCode: Int = -1
|
||||||
override val author: String,
|
override var description: String = ""
|
||||||
override val version: String,
|
|
||||||
override val versionCode: String,
|
|
||||||
override val description: String,
|
|
||||||
val lastUpdate: Long
|
|
||||||
) : MagiskModule
|
|
||||||
|
|
||||||
@Parcelize
|
private val removeFile: SuFile = SuFile(path, "remove")
|
||||||
data class Module(
|
private val disableFile: SuFile = SuFile(path, "disable")
|
||||||
override val id: String,
|
private val updateFile: SuFile = SuFile(path, "update")
|
||||||
override val name: String,
|
|
||||||
override val author: String,
|
|
||||||
override val version: String,
|
|
||||||
override val versionCode: String,
|
|
||||||
override val description: String,
|
|
||||||
val path: String
|
|
||||||
) : MagiskModule
|
|
||||||
|
|
||||||
@AnyThread
|
val updated: Boolean = updateFile.exists()
|
||||||
fun File.toModule(): Single<Module> {
|
|
||||||
val path = "${Const.MAGISK_PATH}/$name"
|
var enable: Boolean = !disableFile.exists()
|
||||||
return "dos2unix < $path/module.prop".su()
|
set(enable) {
|
||||||
.map { it.first().toModule(path) }
|
field = if (enable) {
|
||||||
|
disableFile.delete()
|
||||||
|
} else {
|
||||||
|
!disableFile.createNewFile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var remove: Boolean = removeFile.exists()
|
||||||
|
set(remove) {
|
||||||
|
field = if (remove) {
|
||||||
|
removeFile.createNewFile()
|
||||||
|
} else {
|
||||||
|
!removeFile.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
runCatching {
|
||||||
|
parseProps(Shell.su("dos2unix < $path/module.prop").exec().out)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.isEmpty()) {
|
||||||
|
val sep = path.lastIndexOf('/')
|
||||||
|
id = path.substring(sep + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
name = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
fun loadModules(): List<Module> {
|
||||||
|
val moduleList = mutableListOf<Module>()
|
||||||
|
val path = SuFile(Const.MAGISK_PATH)
|
||||||
|
val modules =
|
||||||
|
path.listFiles { _, name -> name != "lost+found" && name != ".core" }.orEmpty()
|
||||||
|
for (file in modules) {
|
||||||
|
if (file.isFile) continue
|
||||||
|
val module = Module(Const.MAGISK_PATH + "/" + file.name)
|
||||||
|
moduleList.add(module)
|
||||||
|
}
|
||||||
|
return moduleList
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Map<String, String>.toModule(path: String): Module {
|
|
||||||
return Module(
|
|
||||||
id = get("id").orEmpty(),
|
|
||||||
name = get("name").orEmpty(),
|
|
||||||
author = get("author").orEmpty(),
|
|
||||||
version = get("version").orEmpty(),
|
|
||||||
versionCode = get("versionCode").orEmpty(),
|
|
||||||
description = get("description").orEmpty(),
|
|
||||||
path = path
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
fun ResponseBody.toRepository(lastUpdate: Long) = string()
|
|
||||||
.split(Regex("\\n"))
|
|
||||||
.map { it.split("=", limit = 2) }
|
|
||||||
.filter { it.size == 2 }
|
|
||||||
.map { Pair(it[0], it[1]) }
|
|
||||||
.toMap()
|
|
||||||
.toRepository(lastUpdate)
|
|
||||||
|
|
||||||
@AnyThread
|
|
||||||
fun Map<String, String>.toRepository(lastUpdate: Long) = Repository(
|
|
||||||
id = get("id").orEmpty(),
|
|
||||||
name = get("name").orEmpty(),
|
|
||||||
author = get("author").orEmpty(),
|
|
||||||
version = get("version").orEmpty(),
|
|
||||||
versionCode = get("versionCode").orEmpty(),
|
|
||||||
description = get("description").orEmpty(),
|
|
||||||
lastUpdate = lastUpdate
|
|
||||||
)
|
|
@ -9,16 +9,16 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class BaseModule implements Comparable<BaseModule>, Parcelable {
|
public abstract class OldBaseModule implements Comparable<OldBaseModule>, Parcelable {
|
||||||
|
|
||||||
private String mId, mName, mVersion, mAuthor, mDescription;
|
private String mId, mName, mVersion, mAuthor, mDescription;
|
||||||
private int mVersionCode = -1;
|
private int mVersionCode = -1;
|
||||||
|
|
||||||
protected BaseModule() {
|
protected OldBaseModule() {
|
||||||
mId = mName = mVersion = mAuthor = mDescription = "";
|
mId = mName = mVersion = mAuthor = mDescription = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BaseModule(Cursor c) {
|
protected OldBaseModule(Cursor c) {
|
||||||
mId = nonNull(c.getString(c.getColumnIndex("id")));
|
mId = nonNull(c.getString(c.getColumnIndex("id")));
|
||||||
mName = nonNull(c.getString(c.getColumnIndex("name")));
|
mName = nonNull(c.getString(c.getColumnIndex("name")));
|
||||||
mVersion = nonNull(c.getString(c.getColumnIndex("version")));
|
mVersion = nonNull(c.getString(c.getColumnIndex("version")));
|
||||||
@ -27,7 +27,7 @@ public abstract class BaseModule implements Comparable<BaseModule>, Parcelable {
|
|||||||
mDescription = nonNull(c.getString(c.getColumnIndex("description")));
|
mDescription = nonNull(c.getString(c.getColumnIndex("description")));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BaseModule(Parcel p) {
|
protected OldBaseModule(Parcel p) {
|
||||||
mId = p.readString();
|
mId = p.readString();
|
||||||
mName = p.readString();
|
mName = p.readString();
|
||||||
mVersion = p.readString();
|
mVersion = p.readString();
|
||||||
@ -36,17 +36,8 @@ public abstract class BaseModule implements Comparable<BaseModule>, Parcelable {
|
|||||||
mVersionCode = p.readInt();
|
mVersionCode = p.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BaseModule(MagiskModule m) {
|
|
||||||
mId = m.getId();
|
|
||||||
mName = m.getName();
|
|
||||||
mVersion = m.getVersion();
|
|
||||||
mAuthor = m.getAuthor();
|
|
||||||
mDescription = m.getDescription();
|
|
||||||
mVersionCode = Integer.parseInt(m.getVersionCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@NonNull BaseModule module) {
|
public int compareTo(@NonNull OldBaseModule module) {
|
||||||
return getName().toLowerCase().compareTo(module.getName().toLowerCase());
|
return getName().toLowerCase().compareTo(module.getName().toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
@ -1,83 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.model.entity;
|
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
|
||||||
|
|
||||||
public class OldModule extends BaseModule {
|
|
||||||
|
|
||||||
public static final Parcelable.Creator<OldModule> CREATOR = new Creator<OldModule>() {
|
|
||||||
/* It won't be used at any place */
|
|
||||||
@Override
|
|
||||||
public OldModule createFromParcel(Parcel source) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OldModule[] newArray(int size) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private final SuFile mRemoveFile;
|
|
||||||
private final SuFile mDisableFile;
|
|
||||||
private final SuFile mUpdateFile;
|
|
||||||
private final boolean mUpdated;
|
|
||||||
private boolean mEnable;
|
|
||||||
private boolean mRemove;
|
|
||||||
|
|
||||||
public OldModule(String path) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
parseProps(Shell.su("dos2unix < " + path + "/module.prop").exec().getOut());
|
|
||||||
} catch (NumberFormatException ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
mRemoveFile = new SuFile(path, "remove");
|
|
||||||
mDisableFile = new SuFile(path, "disable");
|
|
||||||
mUpdateFile = new SuFile(path, "update");
|
|
||||||
|
|
||||||
if (getId().isEmpty()) {
|
|
||||||
int sep = path.lastIndexOf('/');
|
|
||||||
setId(path.substring(sep + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getName().isEmpty()) {
|
|
||||||
setName(getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
mEnable = !mDisableFile.exists();
|
|
||||||
mRemove = mRemoveFile.exists();
|
|
||||||
mUpdated = mUpdateFile.exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void createDisableFile() {
|
|
||||||
mEnable = !mDisableFile.createNewFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeDisableFile() {
|
|
||||||
mEnable = mDisableFile.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return mEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void createRemoveFile() {
|
|
||||||
mRemove = mRemoveFile.createNewFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteRemoveFile() {
|
|
||||||
mRemove = !mRemoveFile.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean willBeRemoved() {
|
|
||||||
return mRemove;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUpdated() {
|
|
||||||
return mUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -12,7 +12,7 @@ import com.topjohnwu.magisk.utils.XStringKt;
|
|||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class Repo extends BaseModule {
|
public class Repo extends OldBaseModule {
|
||||||
|
|
||||||
private Date mLastUpdate;
|
private Date mLastUpdate;
|
||||||
|
|
||||||
@ -30,11 +30,6 @@ public class Repo extends BaseModule {
|
|||||||
mLastUpdate = new Date(p.readLong());
|
mLastUpdate = new Date(p.readLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Repo(Repository repo) {
|
|
||||||
super(repo);
|
|
||||||
mLastUpdate = new Date(repo.getLastUpdate());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Parcelable.Creator<Repo> CREATOR = new Parcelable.Creator<Repo>() {
|
public static final Parcelable.Creator<Repo> CREATOR = new Parcelable.Creator<Repo>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -107,7 +102,7 @@ public class Repo extends BaseModule {
|
|||||||
return XStringKt.legalFilename(getName() + "-" + getVersion() + ".zip");
|
return XStringKt.legalFilename(getName() + "-" + getVersion() + ".zip");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IllegalRepoException extends Exception {
|
public static class IllegalRepoException extends Exception {
|
||||||
IllegalRepoException(String message) {
|
IllegalRepoException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
@ -6,44 +6,54 @@ import com.skoumal.teanity.databinding.ComparableRvItem
|
|||||||
import com.skoumal.teanity.extensions.addOnPropertyChangedCallback
|
import com.skoumal.teanity.extensions.addOnPropertyChangedCallback
|
||||||
import com.skoumal.teanity.util.KObservableField
|
import com.skoumal.teanity.util.KObservableField
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.model.entity.OldModule
|
import com.topjohnwu.magisk.model.entity.Module
|
||||||
import com.topjohnwu.magisk.model.entity.Repo
|
import com.topjohnwu.magisk.model.entity.Repo
|
||||||
import com.topjohnwu.magisk.model.entity.Repository
|
|
||||||
import com.topjohnwu.magisk.utils.get
|
import com.topjohnwu.magisk.utils.get
|
||||||
import com.topjohnwu.magisk.utils.toggle
|
import com.topjohnwu.magisk.utils.toggle
|
||||||
|
|
||||||
class ModuleRvItem(val item: OldModule) : ComparableRvItem<ModuleRvItem>() {
|
class ModuleRvItem(val item: Module) : ComparableRvItem<ModuleRvItem>() {
|
||||||
|
|
||||||
override val layoutRes: Int = R.layout.item_module
|
override val layoutRes: Int = R.layout.item_module
|
||||||
|
|
||||||
val lastActionNotice = KObservableField("")
|
val lastActionNotice = KObservableField("")
|
||||||
val isChecked = KObservableField(item.isEnabled)
|
val isChecked = KObservableField(item.enable)
|
||||||
val isDeletable = KObservableField(item.willBeRemoved())
|
val isDeletable = KObservableField(item.remove)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
isChecked.addOnPropertyChangedCallback {
|
isChecked.addOnPropertyChangedCallback {
|
||||||
when (it) {
|
when (it) {
|
||||||
true -> item.removeDisableFile().notice(R.string.disable_file_removed)
|
true -> {
|
||||||
false -> item.createDisableFile().notice(R.string.disable_file_created)
|
item.enable = true
|
||||||
|
notice(R.string.disable_file_removed)
|
||||||
|
}
|
||||||
|
false -> {
|
||||||
|
item.enable = false
|
||||||
|
notice(R.string.disable_file_created)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isDeletable.addOnPropertyChangedCallback {
|
isDeletable.addOnPropertyChangedCallback {
|
||||||
when (it) {
|
when (it) {
|
||||||
true -> item.createRemoveFile().notice(R.string.remove_file_created)
|
true -> {
|
||||||
false -> item.deleteRemoveFile().notice(R.string.remove_file_deleted)
|
item.remove = true
|
||||||
|
notice(R.string.remove_file_created)
|
||||||
|
}
|
||||||
|
false -> {
|
||||||
|
item.remove = false
|
||||||
|
notice(R.string.remove_file_deleted)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
item.isUpdated -> notice(R.string.update_file_created)
|
item.updated -> notice(R.string.update_file_created)
|
||||||
item.willBeRemoved() -> notice(R.string.remove_file_created)
|
item.remove -> notice(R.string.remove_file_created)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggle() = isChecked.toggle()
|
fun toggle() = isChecked.toggle()
|
||||||
fun toggleDelete() = isDeletable.toggle()
|
fun toggleDelete() = isDeletable.toggle()
|
||||||
|
|
||||||
@Suppress("unused")
|
private fun notice(@StringRes info: Int) {
|
||||||
private fun Any.notice(@StringRes info: Int) {
|
|
||||||
lastActionNotice.value = get<Resources>().getString(info)
|
lastActionNotice.value = get<Resources>().getString(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +67,6 @@ class ModuleRvItem(val item: OldModule) : ComparableRvItem<ModuleRvItem>() {
|
|||||||
|
|
||||||
class RepoRvItem(val item: Repo) : ComparableRvItem<RepoRvItem>() {
|
class RepoRvItem(val item: Repo) : ComparableRvItem<RepoRvItem>() {
|
||||||
|
|
||||||
constructor(repo: Repository) : this(Repo(repo))
|
|
||||||
|
|
||||||
override val layoutRes: Int = R.layout.item_repo
|
override val layoutRes: Int = R.layout.item_repo
|
||||||
|
|
||||||
override fun contentSameAs(other: RepoRvItem): Boolean = item.version == other.item.version
|
override fun contentSameAs(other: RepoRvItem): Boolean = item.version == other.item.version
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.ui.module
|
package com.topjohnwu.magisk.ui.module
|
||||||
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.database.Cursor
|
|
||||||
import com.skoumal.teanity.databinding.ComparableRvItem
|
import com.skoumal.teanity.databinding.ComparableRvItem
|
||||||
import com.skoumal.teanity.extensions.addOnPropertyChangedCallback
|
import com.skoumal.teanity.extensions.addOnPropertyChangedCallback
|
||||||
import com.skoumal.teanity.extensions.doOnSuccessUi
|
import com.skoumal.teanity.extensions.doOnSuccessUi
|
||||||
@ -11,6 +10,7 @@ import com.skoumal.teanity.util.KObservableField
|
|||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper
|
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper
|
||||||
|
import com.topjohnwu.magisk.model.entity.Module
|
||||||
import com.topjohnwu.magisk.model.entity.Repo
|
import com.topjohnwu.magisk.model.entity.Repo
|
||||||
import com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem
|
import com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem
|
||||||
import com.topjohnwu.magisk.model.entity.recycler.RepoRvItem
|
import com.topjohnwu.magisk.model.entity.recycler.RepoRvItem
|
||||||
@ -20,7 +20,7 @@ import com.topjohnwu.magisk.model.events.OpenChangelogEvent
|
|||||||
import com.topjohnwu.magisk.model.events.OpenFilePickerEvent
|
import com.topjohnwu.magisk.model.events.OpenFilePickerEvent
|
||||||
import com.topjohnwu.magisk.tasks.UpdateRepos
|
import com.topjohnwu.magisk.tasks.UpdateRepos
|
||||||
import com.topjohnwu.magisk.ui.base.MagiskViewModel
|
import com.topjohnwu.magisk.ui.base.MagiskViewModel
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.toList
|
||||||
import com.topjohnwu.magisk.utils.toSingle
|
import com.topjohnwu.magisk.utils.toSingle
|
||||||
import com.topjohnwu.magisk.utils.update
|
import com.topjohnwu.magisk.utils.update
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
@ -59,8 +59,7 @@ class ModuleViewModel(
|
|||||||
fun downloadPressed(item: RepoRvItem) = InstallModuleEvent(item.item).publish()
|
fun downloadPressed(item: RepoRvItem) = InstallModuleEvent(item.item).publish()
|
||||||
|
|
||||||
fun refresh(force: Boolean) {
|
fun refresh(force: Boolean) {
|
||||||
Single.fromCallable { Utils.loadModulesLeanback() }
|
Single.fromCallable { Module.loadModules() }
|
||||||
.map { it.values.toList() }
|
|
||||||
.flattenAsFlowable { it }
|
.flattenAsFlowable { it }
|
||||||
.map { ModuleRvItem(it) }
|
.map { ModuleRvItem(it) }
|
||||||
.toList()
|
.toList()
|
||||||
@ -110,12 +109,6 @@ class ModuleViewModel(
|
|||||||
groupedItems.getOrElse(MODULE_REMOTE) { listOf() }.withTitle(R.string.not_installed)
|
groupedItems.getOrElse(MODULE_REMOTE) { listOf() }.withTitle(R.string.not_installed)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <Result> Cursor.toList(transformer: (Cursor) -> Result): List<Result> {
|
|
||||||
val out = mutableListOf<Result>()
|
|
||||||
while (moveToNext()) out.add(transformer(this))
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
protected const val MODULE_INSTALLED = 0
|
protected const val MODULE_INSTALLED = 0
|
||||||
protected const val MODULE_REMOTE = 1
|
protected const val MODULE_REMOTE = 1
|
||||||
|
@ -10,16 +10,13 @@ import android.content.res.Resources
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.WorkerThread
|
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import com.topjohnwu.magisk.*
|
import com.topjohnwu.magisk.*
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.model.entity.OldModule
|
|
||||||
import com.topjohnwu.magisk.model.update.UpdateCheckService
|
import com.topjohnwu.magisk.model.update.UpdateCheckService
|
||||||
import com.topjohnwu.net.Networking
|
import com.topjohnwu.net.Networking
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
import com.topjohnwu.superuser.io.SuFile
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@ -70,19 +67,6 @@ object Utils {
|
|||||||
return info.loadLabel(pm).toString()
|
return info.loadLabel(pm).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 {
|
fun showSuperUser(): Boolean {
|
||||||
return Shell.rootAccess() && (Const.USER_ID == 0
|
return Shell.rootAccess() && (Const.USER_ID == 0
|
||||||
|| Config.suMultiuserMode != Config.Value.MULTIUSER_MODE_OWNER_MANAGED)
|
|| Config.suMultiuserMode != Config.Value.MULTIUSER_MODE_OWNER_MANAGED)
|
||||||
|
@ -7,6 +7,7 @@ import android.content.pm.ComponentInfo
|
|||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.PackageManager.*
|
import android.content.pm.PackageManager.*
|
||||||
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
import com.topjohnwu.magisk.App
|
import com.topjohnwu.magisk.App
|
||||||
@ -104,3 +105,9 @@ fun String.toFile() = File(this)
|
|||||||
fun Intent.chooser(title: String = "Pick an app") = Intent.createChooser(this, title)
|
fun Intent.chooser(title: String = "Pick an app") = Intent.createChooser(this, title)
|
||||||
|
|
||||||
fun Context.cachedFile(name: String) = File(cacheDir, name)
|
fun Context.cachedFile(name: String) = File(cacheDir, name)
|
||||||
|
|
||||||
|
fun <Result> Cursor.toList(transformer: (Cursor) -> Result): List<Result> {
|
||||||
|
val out = mutableListOf<Result>()
|
||||||
|
while (moveToNext()) out.add(transformer(this))
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
android:id="@+id/version_name"
|
android:id="@+id/version_name"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@{item.item.version == null || item.item.version.length == 0 ? @string/no_info_provided : item.item.version}"
|
android:text="@{item.item.version.length == 0 ? @string/no_info_provided : item.item.version}"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textColor="@android:color/tertiary_text_dark"
|
android:textColor="@android:color/tertiary_text_dark"
|
||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
@ -63,7 +63,7 @@
|
|||||||
android:id="@+id/author"
|
android:id="@+id/author"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@{item.item.author == null || item.item.author.length == 0 ? @string/no_info_provided : @string/author(item.item.author)}"
|
android:text="@{item.item.author.length == 0 ? @string/no_info_provided : @string/author(item.item.author)}"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textColor="@android:color/tertiary_text_dark"
|
android:textColor="@android:color/tertiary_text_dark"
|
||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
@ -79,7 +79,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:text="@{item.item.description == null || item.item.description.length == 0 ? @string/no_info_provided : item.item.description}"
|
android:text="@{item.item.description.length == 0 ? @string/no_info_provided : item.item.description}"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/notice"
|
app:layout_constraintBottom_toTopOf="@+id/notice"
|
||||||
|
Loading…
Reference in New Issue
Block a user