Removed events from modules / replaced with retrofit/rx

This commit is contained in:
Viktor De Pasquale 2019-05-07 15:41:56 +02:00
parent 10e903c9fc
commit 7c755a3991
17 changed files with 116 additions and 223 deletions

View File

@ -1,5 +1,6 @@
package com.topjohnwu.magisk.data.network package com.topjohnwu.magisk.data.network
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.model.entity.GithubRepo import com.topjohnwu.magisk.model.entity.GithubRepo
import io.reactivex.Single import io.reactivex.Single
import retrofit2.http.GET import retrofit2.http.GET
@ -12,7 +13,11 @@ interface GithubApiServices {
fun fetchRepos( fun fetchRepos(
@Query("page") page: Int, @Query("page") page: Int,
@Query("per_page") count: Int = REPOS_PER_PAGE, @Query("per_page") count: Int = REPOS_PER_PAGE,
@Query("sort") sortOrder: String = "pushed" @Query("sort") sortOrder: String = when (Config.get<Int>(Config.Key.REPO_ORDER)) {
Config.Value.ORDER_DATE -> "updated"
Config.Value.ORDER_NAME -> "full_name"
else -> "updated"
}
): Single<List<GithubRepo>> ): Single<List<GithubRepo>>
companion object { companion object {

View File

@ -48,7 +48,7 @@ interface GithubRawApiServices {
@GET("$MAGISK_MODULES/{$MODULE}/master/{$FILE}") @GET("$MAGISK_MODULES/{$MODULE}/master/{$FILE}")
@Streaming @Streaming
fun fetchFile(id: String, file: String): Single<ResponseBody> fun fetchFile(@Path(MODULE) id: String, @Path(FILE) file: String): Single<ResponseBody>
//endregion //endregion

View File

@ -6,6 +6,7 @@ import com.topjohnwu.magisk.data.network.GithubRawApiServices
import com.topjohnwu.magisk.data.network.GithubServices import com.topjohnwu.magisk.data.network.GithubServices
import com.topjohnwu.magisk.model.entity.GithubRepo import com.topjohnwu.magisk.model.entity.GithubRepo
import com.topjohnwu.magisk.model.entity.toRepository import com.topjohnwu.magisk.model.entity.toRepository
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.writeToFile import com.topjohnwu.magisk.utils.writeToFile
import com.topjohnwu.magisk.utils.writeToString import com.topjohnwu.magisk.utils.writeToString
import io.reactivex.Single import io.reactivex.Single
@ -18,9 +19,16 @@ class ModuleRepository(
) { ) {
fun fetchModules() = fetchAllRepos() fun fetchModules() = fetchAllRepos()
.flattenAsFlowable { it } .map {
.flatMapSingle { fetchProperties(it.name, it.updatedAtMillis) } it.mapNotNull {
.toList() runCatching {
fetchProperties(it.name, it.updatedAtMillis).blockingGet()
}.getOrNull()
}
}
fun fetchInstalledModules() = Single.fromCallable { Utils.loadModulesLeanback() }
.map { it.values.toList() }
fun fetchInstallFile(module: String) = apiRaw fun fetchInstallFile(module: String) = apiRaw
.fetchFile(module, FILE_INSTALL_SH) .fetchFile(module, FILE_INSTALL_SH)

View File

@ -36,9 +36,18 @@ 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 BaseModule module) {
return this.getName().toLowerCase().compareTo(module.getName().toLowerCase()); return getName().toLowerCase().compareTo(module.getName().toLowerCase());
} }
@Override @Override
@ -71,7 +80,9 @@ public abstract class BaseModule implements Comparable<BaseModule>, Parcelable {
return values; return values;
} }
protected void parseProps(List<String> props) { parseProps(props.toArray(new String[0])); } protected void parseProps(List<String> props) {
parseProps(props.toArray(new String[0]));
}
protected void parseProps(String[] props) throws NumberFormatException { protected void parseProps(String[] props) throws NumberFormatException {
for (String line : props) { for (String line : props) {

View File

@ -3,10 +3,14 @@ package com.topjohnwu.magisk.model.entity
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.topjohnwu.magisk.utils.timeFormatStandard import com.topjohnwu.magisk.utils.timeFormatStandard
import com.topjohnwu.magisk.utils.toTime import com.topjohnwu.magisk.utils.toTime
import timber.log.Timber
data class GithubRepo( data class GithubRepo(
@Json(name = "name") val name: String, @Json(name = "name") val name: String,
@Json(name = "updated_at") val updatedAt: String @Json(name = "updated_at") val updatedAt: String
) { ) {
val updatedAtMillis by lazy { updatedAt.toTime(timeFormatStandard) } val updatedAtMillis by lazy {
updatedAt.toTime(timeFormatStandard)
.apply { Timber.i("$name updated @ $this ($updatedAt)") }
}
} }

View File

@ -19,6 +19,7 @@ interface MagiskModule : Parcelable {
val author: String val author: String
val version: String val version: String
val versionCode: String val versionCode: String
val description: String
} }
@Entity(tableName = "repos") @Entity(tableName = "repos")
@ -30,6 +31,7 @@ data class Repository(
override val author: String, override val author: String,
override val version: String, override val version: String,
override val versionCode: String, override val versionCode: String,
override val description: String,
val lastUpdate: Long val lastUpdate: Long
) : MagiskModule ) : MagiskModule
@ -40,6 +42,7 @@ data class Module(
override val author: String, override val author: String,
override val version: String, override val version: String,
override val versionCode: String, override val versionCode: String,
override val description: String,
val path: String val path: String
) : MagiskModule ) : MagiskModule
@ -57,6 +60,7 @@ fun Map<String, String>.toModule(path: String): Module {
author = get("author").orEmpty(), author = get("author").orEmpty(),
version = get("version").orEmpty(), version = get("version").orEmpty(),
versionCode = get("versionCode").orEmpty(), versionCode = get("versionCode").orEmpty(),
description = get("description").orEmpty(),
path = path path = path
) )
} }
@ -77,5 +81,6 @@ fun Map<String, String>.toRepository(lastUpdate: Long) = Repository(
author = get("author").orEmpty(), author = get("author").orEmpty(),
version = get("version").orEmpty(), version = get("version").orEmpty(),
versionCode = get("versionCode").orEmpty(), versionCode = get("versionCode").orEmpty(),
description = get("description").orEmpty(),
lastUpdate = lastUpdate lastUpdate = lastUpdate
) )

View File

@ -29,6 +29,11 @@ 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
@ -49,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.dlString(getPropUrl()).split("\\n");
try { try {
parseProps(props); parseProps(props);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {

View File

@ -8,6 +8,7 @@ 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.OldModule
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
@ -55,6 +56,8 @@ 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

View File

@ -1,33 +0,0 @@
package com.topjohnwu.magisk.model.update;
import androidx.annotation.NonNull;
import androidx.work.ListenableWorker;
import com.topjohnwu.magisk.App;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Config;
import com.topjohnwu.magisk.model.worker.DelegateWorker;
import com.topjohnwu.magisk.tasks.CheckUpdates;
import com.topjohnwu.magisk.view.Notifications;
import com.topjohnwu.superuser.Shell;
public class UpdateCheckService extends DelegateWorker {
@NonNull
@Override
public ListenableWorker.Result doWork() {
if (App.foreground() == null) {
Shell.getShell();
CheckUpdates.check(this::onCheckDone);
}
return ListenableWorker.Result.success();
}
private void onCheckDone() {
if (BuildConfig.VERSION_CODE < Config.remoteManagerVersionCode) {
Notifications.managerUpdate();
} else if (Config.magiskVersionCode < Config.remoteMagiskVersionCode) {
Notifications.magiskUpdate();
}
}
}

View File

@ -0,0 +1,31 @@
package com.topjohnwu.magisk.model.update
import androidx.work.ListenableWorker
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.data.repository.MagiskRepository
import com.topjohnwu.magisk.model.entity.MagiskConfig
import com.topjohnwu.magisk.model.worker.DelegateWorker
import com.topjohnwu.magisk.utils.inject
import com.topjohnwu.magisk.view.Notifications
class UpdateCheckService : DelegateWorker() {
private val magiskRepo: MagiskRepository by inject()
override fun doWork(): ListenableWorker.Result {
val config = runCatching { magiskRepo.fetchConfig().blockingGet() }
config.getOrNull()?.let { checkUpdates(it) }
return when {
config.isFailure -> ListenableWorker.Result.failure()
else -> ListenableWorker.Result.success()
}
}
private fun checkUpdates(config: MagiskConfig) {
when {
BuildConfig.VERSION_CODE < config.app.versionCode.toIntOrNull() ?: -1 -> Notifications.managerUpdate()
Config.magiskVersionCode < config.magisk.versionCode.toIntOrNull() ?: -1 -> Notifications.magiskUpdate()
}
}
}

View File

@ -1,133 +0,0 @@
package com.topjohnwu.magisk.tasks;
import android.os.SystemClock;
import com.topjohnwu.magisk.Config;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.utils.Event;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.Request;
import com.topjohnwu.net.ResponseListener;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.internal.UiThreadHandler;
import org.json.JSONException;
import org.json.JSONObject;
@Deprecated
public class CheckUpdates {
private static Request getRequest() {
String url;
switch ((int) Config.get(Config.Key.UPDATE_CHANNEL)) {
case Config.Value.BETA_CHANNEL:
url = Const.Url.BETA_URL;
break;
case Config.Value.CUSTOM_CHANNEL:
url = Config.get(Config.Key.CUSTOM_CHANNEL);
break;
case Config.Value.CANARY_CHANNEL:
url = Const.Url.CANARY_URL;
break;
case Config.Value.CANARY_DEBUG_CHANNEL:
url = Const.Url.CANARY_DEBUG_URL;
break;
default:
url = Const.Url.STABLE_URL;
break;
}
return Networking.get(url);
}
public static void check() {
check(null);
}
public static void check(Runnable cb) {
Request request = getRequest();
UpdateListener listener = new UpdateListener(cb);
if (ShellUtils.onMainThread()) {
request.getAsJSONObject(listener);
} else {
JSONObject json = request.execForJSONObject().getResult();
if (json != null)
listener.onResponse(json);
}
}
private static class UpdateListener implements ResponseListener<JSONObject> {
private final Runnable cb;
private final long start;
UpdateListener(Runnable callback) {
cb = callback;
start = SystemClock.uptimeMillis();
}
private int getInt(JSONObject json, String name, int defValue) {
if (json == null)
return defValue;
try {
return json.getInt(name);
} catch (JSONException e) {
return defValue;
}
}
private String getString(JSONObject json, String name, String defValue) {
if (json == null)
return defValue;
try {
return json.getString(name);
} catch (JSONException e) {
return defValue;
}
}
private JSONObject getJson(JSONObject json, String name) {
try {
return json.getJSONObject(name);
} catch (JSONException e) {
return null;
}
}
@Override
public void onResponse(JSONObject json) {
JSONObject magisk = getJson(json, "magisk");
Config.remoteMagiskVersionCode = getInt(magisk, "versionCode", -1);
if ((int) Config.get(Config.Key.UPDATE_CHANNEL) == Config.Value.DEFAULT_CHANNEL) {
if (Config.magiskVersionCode > Config.remoteMagiskVersionCode) {
// If we are newer than current stable channel, switch to beta
Config.set(Config.Key.UPDATE_CHANNEL, Config.Value.BETA_CHANNEL);
check(cb);
return;
} else {
Config.set(Config.Key.UPDATE_CHANNEL, Config.Value.STABLE_CHANNEL);
}
}
Config.remoteMagiskVersionString = getString(magisk, "version", null);
Config.magiskLink = getString(magisk, "link", null);
Config.magiskNoteLink = getString(magisk, "note", null);
Config.magiskMD5 = getString(magisk, "md5", null);
JSONObject manager = getJson(json, "app");
Config.remoteManagerVersionString = getString(manager, "version", null);
Config.remoteManagerVersionCode = getInt(manager, "versionCode", -1);
Config.managerLink = getString(manager, "link", null);
Config.managerNoteLink = getString(manager, "note", null);
JSONObject uninstaller = getJson(json, "uninstaller");
Config.uninstallerLink = getString(uninstaller, "link", null);
UiThreadHandler.handler.postAtTime(() -> Event.trigger(Event.UPDATE_CHECK_DONE),
start + 1000 /* Add artificial delay to let UI behave correctly */);
if (cb != null)
cb.run();
}
}
}

View File

@ -6,7 +6,6 @@ import android.text.TextUtils
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.topjohnwu.magisk.* import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.tasks.CheckUpdates
import com.topjohnwu.magisk.tasks.UpdateRepos import com.topjohnwu.magisk.tasks.UpdateRepos
import com.topjohnwu.magisk.utils.LocaleManager import com.topjohnwu.magisk.utils.LocaleManager
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
@ -59,7 +58,7 @@ open class SplashActivity : AppCompatActivity() {
// Schedule periodic update checks // Schedule periodic update checks
Utils.scheduleUpdateCheck() Utils.scheduleUpdateCheck()
CheckUpdates.check() //CheckUpdates.check()
// Setup shortcuts // Setup shortcuts
Shortcuts.setup(this) Shortcuts.setup(this)

View File

@ -10,28 +10,22 @@ import com.skoumal.teanity.util.DiffObservableList
import com.skoumal.teanity.util.KObservableField 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.repository.ModuleRepository
import com.topjohnwu.magisk.model.entity.OldModule
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
import com.topjohnwu.magisk.model.entity.recycler.SectionRvItem import com.topjohnwu.magisk.model.entity.recycler.SectionRvItem
import com.topjohnwu.magisk.model.events.InstallModuleEvent import com.topjohnwu.magisk.model.events.InstallModuleEvent
import com.topjohnwu.magisk.model.events.OpenChangelogEvent 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.ui.base.MagiskViewModel import com.topjohnwu.magisk.ui.base.MagiskViewModel
import com.topjohnwu.magisk.utils.Event
import com.topjohnwu.magisk.utils.Utils
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.disposables.Disposable import io.reactivex.disposables.Disposable
import me.tatarka.bindingcollectionadapter2.OnItemBind import me.tatarka.bindingcollectionadapter2.OnItemBind
class ModuleViewModel( class ModuleViewModel(
private val repoDatabase: RepoDatabaseHelper, private val resources: Resources,
private val resources: Resources private val moduleRepo: ModuleRepository
) : MagiskViewModel() { ) : MagiskViewModel() {
val query = KObservableField("") val query = KObservableField("")
@ -52,36 +46,15 @@ class ModuleViewModel(
queryDisposable?.dispose() queryDisposable?.dispose()
queryDisposable = query() queryDisposable = query()
} }
Event.register(this)
refresh() refresh()
} }
override fun getListeningEvents(): IntArray {
return intArrayOf(Event.MODULE_LOAD_DONE, Event.REPO_LOAD_DONE)
}
override fun onEvent(event: Int) = when (event) {
Event.MODULE_LOAD_DONE -> updateModules(Event.getResult(event))
Event.REPO_LOAD_DONE -> updateRepos()
else -> Unit
}
fun fabPressed() = OpenFilePickerEvent().publish() fun fabPressed() = OpenFilePickerEvent().publish()
fun repoPressed(item: RepoRvItem) = OpenChangelogEvent(item.item).publish() fun repoPressed(item: RepoRvItem) = OpenChangelogEvent(item.item).publish()
fun downloadPressed(item: RepoRvItem) = InstallModuleEvent(item.item).publish() fun downloadPressed(item: RepoRvItem) = InstallModuleEvent(item.item).publish()
fun refresh() { fun refresh() {
state = State.LOADING moduleRepo.fetchModules()
Utils.loadModules(true)
UpdateRepos().exec(true)
}
private fun updateModules(result: Map<String, OldModule>) = result.values
.map { ModuleRvItem(it) }
.let { itemsInstalled.update(it) }
internal fun updateRepos() {
Single.fromCallable { repoDatabase.repoCursor.toList { Repo(it) } }
.flattenAsFlowable { it } .flattenAsFlowable { it }
.map { RepoRvItem(it) } .map { RepoRvItem(it) }
.toList() .toList()
@ -89,7 +62,13 @@ class ModuleViewModel(
.flatMap { queryRaw() } .flatMap { queryRaw() }
.applyViewModel(this) .applyViewModel(this)
.subscribeK { itemsRemote.update(it.first, it.second) } .subscribeK { itemsRemote.update(it.first, it.second) }
.add()
moduleRepo.fetchInstalledModules()
.flattenAsFlowable { it }
.map { ModuleRvItem(it) }
.toList()
.map { it to itemsInstalled.calculateDiff(it) }
.subscribeK { itemsInstalled.update(it.first, it.second) }
} }
private fun query() = queryRaw() private fun query() = queryRaw()

View File

@ -56,7 +56,7 @@ class ReposFragment : MagiskFragment<ModuleViewModel, FragmentReposBinding>(),
Config.get<Int>(Config.Key.REPO_ORDER)!! Config.get<Int>(Config.Key.REPO_ORDER)!!
) { d, which -> ) { d, which ->
Config.set(Config.Key.REPO_ORDER, which) Config.set(Config.Key.REPO_ORDER, which)
viewModel.updateRepos() viewModel.refresh()
d.dismiss() d.dismiss()
}.show() }.show()
} }

View File

@ -12,7 +12,6 @@ import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Config; import com.topjohnwu.magisk.Config;
import com.topjohnwu.magisk.Const; import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.tasks.CheckUpdates;
import com.topjohnwu.magisk.ui.base.BasePreferenceFragment; import com.topjohnwu.magisk.ui.base.BasePreferenceFragment;
import com.topjohnwu.magisk.utils.DownloadApp; import com.topjohnwu.magisk.utils.DownloadApp;
import com.topjohnwu.magisk.utils.Event; import com.topjohnwu.magisk.utils.Event;
@ -218,7 +217,7 @@ public class SettingsFragment extends BasePreferenceFragment {
break; break;
case Config.Key.UPDATE_CHANNEL: case Config.Key.UPDATE_CHANNEL:
case Config.Key.CUSTOM_CHANNEL: case Config.Key.CUSTOM_CHANNEL:
CheckUpdates.check(); //CheckUpdates.check();
break; break;
case Config.Key.CHECK_UPDATES: case Config.Key.CHECK_UPDATES:
Utils.scheduleUpdateCheck(); Utils.scheduleUpdateCheck();

View File

@ -27,6 +27,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import androidx.annotation.WorkerThread;
import androidx.work.Constraints; import androidx.work.Constraints;
import androidx.work.ExistingPeriodicWorkPolicy; import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.NetworkType; import androidx.work.NetworkType;
@ -84,22 +85,16 @@ public class Utils {
.replace("#", "").replace("@", "").replace("\\", "_"); .replace("#", "").replace("@", "").replace("\\", "_");
} }
@Deprecated
public static void loadModules() { public static void loadModules() {
loadModules(true); loadModules(true);
} }
@Deprecated
public static void loadModules(boolean async) { public static void loadModules(boolean async) {
Event.reset(Event.MODULE_LOAD_DONE); Event.reset(Event.MODULE_LOAD_DONE);
Runnable run = () -> { Runnable run = () -> {
Map<String, OldModule> moduleMap = new ValueSortedMap<>(); Map<String, OldModule> moduleMap = loadModulesLeanback();
SuFile path = new SuFile(Const.MAGISK_PATH);
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);
}
Event.trigger(Event.MODULE_LOAD_DONE, moduleMap); Event.trigger(Event.MODULE_LOAD_DONE, moduleMap);
}; };
if (async) if (async)
@ -108,6 +103,21 @@ public class Utils {
run.run(); run.run();
} }
@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() { public static boolean showSuperUser() {
return Shell.rootAccess() && (Const.USER_ID == 0 || return Shell.rootAccess() && (Const.USER_ID == 0 ||
(int) Config.get(Config.Key.SU_MULTIUSER_MODE) != (int) Config.get(Config.Key.SU_MULTIUSER_MODE) !=

View File

@ -14,5 +14,5 @@ fun String.toTime(format: SimpleDateFormat) = try {
} }
private val locale get() = Locale.getDefault() private val locale get() = Locale.getDefault()
val timeFormatFull by lazy { SimpleDateFormat("YYYY/MM/DD_HH:mm:ss", locale) } val timeFormatFull by lazy { SimpleDateFormat("yyyy/MM/dd_HH:mm:ss", locale) }
val timeFormatStandard by lazy { SimpleDateFormat("YYYY-MM-DD'T'HH:mm:ss'Z'", locale) } val timeFormatStandard by lazy { SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", locale) }