You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Modernize Repo class for Magisk modules
- Use Kotlin - Use Room database - Use retrofit for networking - Use RxJava pipeline for repo updating
This commit is contained in:
@@ -5,7 +5,7 @@ import androidx.core.app.NotificationCompat
|
||||
import com.skoumal.teanity.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.Config
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.data.repository.FileRepository
|
||||
import com.topjohnwu.magisk.data.network.GithubRawServices
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Magisk
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Module
|
||||
@@ -24,10 +24,10 @@ import java.io.InputStream
|
||||
|
||||
abstract class RemoteFileService : NotificationService() {
|
||||
|
||||
private val repo by inject<FileRepository>()
|
||||
private val service: GithubRawServices by inject()
|
||||
|
||||
private val supportedFolders
|
||||
get() = listOfNotNull(
|
||||
get() = listOf(
|
||||
cacheDir,
|
||||
Config.downloadDirectory
|
||||
)
|
||||
@@ -68,11 +68,11 @@ abstract class RemoteFileService : NotificationService() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun download(subject: DownloadSubject) = repo.downloadFile(subject.url)
|
||||
private fun download(subject: DownloadSubject) = service.fetchFile(subject.url)
|
||||
.map { it.toStream(subject.hashCode()) }
|
||||
.flatMap { stream ->
|
||||
when (subject) {
|
||||
is Module -> repo.downloadInstaller()
|
||||
is Module -> service.fetchInstaller()
|
||||
.map { stream.toModule(subject.file, it.byteStream()); subject.file }
|
||||
else -> Single.fromCallable { stream.writeTo(subject.file); subject.file }
|
||||
}
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
package com.topjohnwu.magisk.model.entity;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class OldBaseModule implements Comparable<OldBaseModule>, Parcelable {
|
||||
|
||||
private String mId, mName, mVersion, mAuthor, mDescription;
|
||||
private int mVersionCode = -1;
|
||||
|
||||
protected OldBaseModule() {
|
||||
mId = mName = mVersion = mAuthor = mDescription = "";
|
||||
}
|
||||
|
||||
protected OldBaseModule(Cursor c) {
|
||||
mId = nonNull(c.getString(c.getColumnIndex("id")));
|
||||
mName = nonNull(c.getString(c.getColumnIndex("name")));
|
||||
mVersion = nonNull(c.getString(c.getColumnIndex("version")));
|
||||
mVersionCode = c.getInt(c.getColumnIndex("versionCode"));
|
||||
mAuthor = nonNull(c.getString(c.getColumnIndex("author")));
|
||||
mDescription = nonNull(c.getString(c.getColumnIndex("description")));
|
||||
}
|
||||
|
||||
protected OldBaseModule(Parcel p) {
|
||||
mId = p.readString();
|
||||
mName = p.readString();
|
||||
mVersion = p.readString();
|
||||
mAuthor = p.readString();
|
||||
mDescription = p.readString();
|
||||
mVersionCode = p.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull OldBaseModule module) {
|
||||
return getName().toLowerCase().compareTo(module.getName().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mId);
|
||||
dest.writeString(mName);
|
||||
dest.writeString(mVersion);
|
||||
dest.writeString(mAuthor);
|
||||
dest.writeString(mDescription);
|
||||
dest.writeInt(mVersionCode);
|
||||
}
|
||||
|
||||
private String nonNull(String s) {
|
||||
return s == null ? "" : s;
|
||||
}
|
||||
|
||||
public ContentValues getContentValues() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("id", mId);
|
||||
values.put("name", mName);
|
||||
values.put("version", mVersion);
|
||||
values.put("versionCode", mVersionCode);
|
||||
values.put("author", mAuthor);
|
||||
values.put("description", mDescription);
|
||||
return values;
|
||||
}
|
||||
|
||||
protected void parseProps(List<String> props) {
|
||||
parseProps(props.toArray(new String[0]));
|
||||
}
|
||||
|
||||
protected void parseProps(String[] props) throws NumberFormatException {
|
||||
for (String line : props) {
|
||||
String[] prop = line.split("=", 2);
|
||||
if (prop.length != 2)
|
||||
continue;
|
||||
|
||||
String key = prop[0].trim();
|
||||
String value = prop[1].trim();
|
||||
if (key.isEmpty() || key.charAt(0) == '#')
|
||||
continue;
|
||||
|
||||
switch (key) {
|
||||
case "id":
|
||||
mId = value;
|
||||
break;
|
||||
case "name":
|
||||
mName = value;
|
||||
break;
|
||||
case "version":
|
||||
mVersion = value;
|
||||
break;
|
||||
case "versionCode":
|
||||
mVersionCode = Integer.parseInt(value);
|
||||
break;
|
||||
case "author":
|
||||
mAuthor = value;
|
||||
break;
|
||||
case "description":
|
||||
mDescription = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return mAuthor;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public int getVersionCode() {
|
||||
return mVersionCode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package com.topjohnwu.magisk.model.entity;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.topjohnwu.magisk.Const;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.XStringKt;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class Repo extends OldBaseModule {
|
||||
|
||||
private Date mLastUpdate;
|
||||
|
||||
public Repo(String id) {
|
||||
setId(id);
|
||||
}
|
||||
|
||||
public Repo(Cursor c) {
|
||||
super(c);
|
||||
mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update")));
|
||||
}
|
||||
|
||||
public Repo(Parcel p) {
|
||||
super(p);
|
||||
mLastUpdate = new Date(p.readLong());
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Repo> CREATOR = new Parcelable.Creator<Repo>() {
|
||||
|
||||
@Override
|
||||
public Repo createFromParcel(Parcel source) {
|
||||
return new Repo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repo[] newArray(int size) {
|
||||
return new Repo[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeLong(mLastUpdate.getTime());
|
||||
}
|
||||
|
||||
public void update() throws IllegalRepoException {
|
||||
String[] props = Utils.INSTANCE.dlString(getPropUrl()).split("\\n");
|
||||
try {
|
||||
parseProps(props);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalRepoException("Repo [" + getId() + "] parse error: " + e.getMessage());
|
||||
}
|
||||
|
||||
if (getVersionCode() < 0) {
|
||||
throw new IllegalRepoException("Repo [" + getId() + "] does not contain versionCode");
|
||||
}
|
||||
}
|
||||
|
||||
public void update(Date lastUpdate) throws IllegalRepoException {
|
||||
mLastUpdate = lastUpdate;
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentValues getContentValues() {
|
||||
ContentValues values = super.getContentValues();
|
||||
values.put("last_update", mLastUpdate.getTime());
|
||||
return values;
|
||||
}
|
||||
|
||||
public String getZipUrl() {
|
||||
return String.format(Const.Url.ZIP_URL, getId());
|
||||
}
|
||||
|
||||
public String getPropUrl() {
|
||||
return getFileUrl("module.prop");
|
||||
}
|
||||
|
||||
public String getDetailUrl() {
|
||||
return getFileUrl("README.md");
|
||||
}
|
||||
|
||||
public String getFileUrl(String file) {
|
||||
return String.format(Const.Url.FILE_URL, getId(), file);
|
||||
}
|
||||
|
||||
public String getLastUpdateString() {
|
||||
return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(mLastUpdate);
|
||||
}
|
||||
|
||||
public Date getLastUpdate() {
|
||||
return mLastUpdate;
|
||||
}
|
||||
|
||||
public String getDownloadFilename() {
|
||||
return XStringKt.legalFilename(getName() + "-" + getVersion() + ".zip");
|
||||
}
|
||||
|
||||
public static class IllegalRepoException extends Exception {
|
||||
IllegalRepoException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import android.os.Parcelable
|
||||
import com.topjohnwu.magisk.Config
|
||||
import com.topjohnwu.magisk.Info
|
||||
import com.topjohnwu.magisk.model.entity.MagiskJson
|
||||
import com.topjohnwu.magisk.model.entity.Repo
|
||||
import com.topjohnwu.magisk.model.entity.module.Repo
|
||||
import com.topjohnwu.magisk.utils.cachedFile
|
||||
import com.topjohnwu.magisk.utils.get
|
||||
import kotlinx.android.parcel.IgnoredOnParcel
|
||||
@@ -20,8 +20,8 @@ sealed class DownloadSubject : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
data class Module(
|
||||
val module: Repo,
|
||||
val configuration: Configuration
|
||||
val module: Repo,
|
||||
val configuration: Configuration
|
||||
) : DownloadSubject() {
|
||||
override val url: String get() = module.zipUrl
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.topjohnwu.magisk.model.entity.module
|
||||
|
||||
abstract class BaseModule : Comparable<BaseModule> {
|
||||
abstract var id: String
|
||||
protected set
|
||||
abstract var name: String
|
||||
protected set
|
||||
abstract var author: 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)
|
||||
}
|
||||
+1
-41
@@ -1,50 +1,10 @@
|
||||
package com.topjohnwu.magisk.model.entity
|
||||
package com.topjohnwu.magisk.model.entity.module
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.topjohnwu.magisk.Const
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.io.SuFile
|
||||
|
||||
abstract class BaseModule : Comparable<BaseModule> {
|
||||
abstract var id: String
|
||||
protected set
|
||||
abstract var name: String
|
||||
protected set
|
||||
abstract var author: 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)
|
||||
}
|
||||
|
||||
class Module(path: String) : BaseModule() {
|
||||
override var id: String = ""
|
||||
override var name: String = ""
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.topjohnwu.magisk.model.entity.module
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.topjohnwu.magisk.Const
|
||||
import com.topjohnwu.magisk.data.repository.StringRepository
|
||||
import com.topjohnwu.magisk.utils.get
|
||||
import com.topjohnwu.magisk.utils.legalFilename
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
@Entity(tableName = "repos")
|
||||
@Parcelize
|
||||
data class Repo(
|
||||
@PrimaryKey override var id: String,
|
||||
override var name: String,
|
||||
override var author: String,
|
||||
override var version: String,
|
||||
override var versionCode: Int,
|
||||
override var description: String,
|
||||
var last_update: Long
|
||||
) : BaseModule(), Parcelable {
|
||||
|
||||
private val stringRepo: StringRepository get() = get()
|
||||
|
||||
val lastUpdate get() = Date(last_update)
|
||||
|
||||
val lastUpdateString: String get() =
|
||||
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(lastUpdate)
|
||||
|
||||
val downloadFilename: String get() = "$name-$version.zip".legalFilename()
|
||||
|
||||
val readme get() = stringRepo.getReadme(this)
|
||||
|
||||
val zipUrl: String get() = Const.Url.ZIP_URL.format(id)
|
||||
|
||||
constructor(id: String) : this(id, "", "", "", -1, "", 0)
|
||||
|
||||
@Throws(IllegalRepoException::class)
|
||||
fun update() {
|
||||
val props = runCatching {
|
||||
stringRepo.getMetadata(this).blockingGet()
|
||||
.orEmpty().split("\\n".toRegex()).dropLastWhile { it.isEmpty() }
|
||||
}.getOrElse {
|
||||
throw IllegalRepoException("Repo [$id] module.prop download error: " + it.message)
|
||||
}
|
||||
|
||||
props.runCatching {
|
||||
parseProps(this)
|
||||
}.onFailure {
|
||||
throw IllegalRepoException("Repo [$id] parse error: " + it.message)
|
||||
}
|
||||
|
||||
if (versionCode < 0) {
|
||||
throw IllegalRepoException("Repo [$id] does not contain versionCode")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IllegalRepoException::class)
|
||||
fun update(lastUpdate: Date) {
|
||||
last_update = lastUpdate.time
|
||||
update()
|
||||
}
|
||||
|
||||
class IllegalRepoException(message: String) : Exception(message)
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import com.skoumal.teanity.databinding.ComparableRvItem
|
||||
import com.skoumal.teanity.extensions.addOnPropertyChangedCallback
|
||||
import com.skoumal.teanity.util.KObservableField
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.model.entity.Module
|
||||
import com.topjohnwu.magisk.model.entity.Repo
|
||||
import com.topjohnwu.magisk.model.entity.module.Module
|
||||
import com.topjohnwu.magisk.model.entity.module.Repo
|
||||
import com.topjohnwu.magisk.utils.get
|
||||
import com.topjohnwu.magisk.utils.toggle
|
||||
|
||||
@@ -69,11 +69,7 @@ class RepoRvItem(val item: Repo) : ComparableRvItem<RepoRvItem>() {
|
||||
|
||||
override val layoutRes: Int = R.layout.item_repo
|
||||
|
||||
override fun contentSameAs(other: RepoRvItem): Boolean = item.version == other.item.version
|
||||
&& item.lastUpdate == other.item.lastUpdate
|
||||
&& item.versionCode == other.item.versionCode
|
||||
&& item.description == other.item.description
|
||||
&& item.detailUrl == other.item.detailUrl
|
||||
override fun contentSameAs(other: RepoRvItem): Boolean = item == other.item
|
||||
|
||||
override fun itemSameAs(other: RepoRvItem): Boolean = item.id == other.item.id
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.model.events
|
||||
import android.app.Activity
|
||||
import com.skoumal.teanity.viewevents.ViewEvent
|
||||
import com.topjohnwu.magisk.model.entity.Policy
|
||||
import com.topjohnwu.magisk.model.entity.Repo
|
||||
import com.topjohnwu.magisk.model.entity.module.Repo
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user