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
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.model.entity.GithubRepo
import io.reactivex.Single
import retrofit2.http.GET
@ -12,7 +13,11 @@ interface GithubApiServices {
fun fetchRepos(
@Query("page") page: Int,
@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>>
companion object {

View File

@ -48,7 +48,7 @@ interface GithubRawApiServices {
@GET("$MAGISK_MODULES/{$MODULE}/master/{$FILE}")
@Streaming
fun fetchFile(id: String, file: String): Single<ResponseBody>
fun fetchFile(@Path(MODULE) id: String, @Path(FILE) file: String): Single<ResponseBody>
//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.model.entity.GithubRepo
import com.topjohnwu.magisk.model.entity.toRepository
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.writeToFile
import com.topjohnwu.magisk.utils.writeToString
import io.reactivex.Single
@ -18,9 +19,16 @@ class ModuleRepository(
) {
fun fetchModules() = fetchAllRepos()
.flattenAsFlowable { it }
.flatMapSingle { fetchProperties(it.name, it.updatedAtMillis) }
.toList()
.map {
it.mapNotNull {
runCatching {
fetchProperties(it.name, it.updatedAtMillis).blockingGet()
}.getOrNull()
}
}
fun fetchInstalledModules() = Single.fromCallable { Utils.loadModulesLeanback() }
.map { it.values.toList() }
fun fetchInstallFile(module: String) = apiRaw
.fetchFile(module, FILE_INSTALL_SH)

View File

@ -36,9 +36,18 @@ public abstract class BaseModule implements Comparable<BaseModule>, Parcelable {
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
public int compareTo(@NonNull BaseModule module) {
return this.getName().toLowerCase().compareTo(module.getName().toLowerCase());
return getName().toLowerCase().compareTo(module.getName().toLowerCase());
}
@Override
@ -71,7 +80,9 @@ public abstract class BaseModule implements Comparable<BaseModule>, Parcelable {
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 {
for (String line : props) {

View File

@ -3,10 +3,14 @@ package com.topjohnwu.magisk.model.entity
import com.squareup.moshi.Json
import com.topjohnwu.magisk.utils.timeFormatStandard
import com.topjohnwu.magisk.utils.toTime
import timber.log.Timber
data class GithubRepo(
@Json(name = "name") val name: 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 version: String
val versionCode: String
val description: String
}
@Entity(tableName = "repos")
@ -30,6 +31,7 @@ data class Repository(
override val author: String,
override val version: String,
override val versionCode: String,
override val description: String,
val lastUpdate: Long
) : MagiskModule
@ -40,6 +42,7 @@ data class Module(
override val author: String,
override val version: String,
override val versionCode: String,
override val description: String,
val path: String
) : MagiskModule
@ -57,6 +60,7 @@ fun Map<String, String>.toModule(path: String): Module {
author = get("author").orEmpty(),
version = get("version").orEmpty(),
versionCode = get("versionCode").orEmpty(),
description = get("description").orEmpty(),
path = path
)
}
@ -77,5 +81,6 @@ fun Map<String, String>.toRepository(lastUpdate: Long) = Repository(
author = get("author").orEmpty(),
version = get("version").orEmpty(),
versionCode = get("versionCode").orEmpty(),
description = get("description").orEmpty(),
lastUpdate = lastUpdate
)

View File

@ -29,6 +29,11 @@ public class Repo extends BaseModule {
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>() {
@Override
@ -49,7 +54,7 @@ public class Repo extends BaseModule {
}
public void update() throws IllegalRepoException {
String props[] = Utils.dlString(getPropUrl()).split("\\n");
String[] props = Utils.dlString(getPropUrl()).split("\\n");
try {
parseProps(props);
} catch (NumberFormatException e) {

View File

@ -8,6 +8,7 @@ import com.skoumal.teanity.util.KObservableField
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.model.entity.OldModule
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.toggle
@ -55,6 +56,8 @@ class ModuleRvItem(val item: OldModule) : ComparableRvItem<ModuleRvItem>() {
class RepoRvItem(val item: Repo) : ComparableRvItem<RepoRvItem>() {
constructor(repo: Repository) : this(Repo(repo))
override val layoutRes: Int = R.layout.item_repo
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.AppCompatActivity
import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.tasks.CheckUpdates
import com.topjohnwu.magisk.tasks.UpdateRepos
import com.topjohnwu.magisk.utils.LocaleManager
import com.topjohnwu.magisk.utils.Utils
@ -59,7 +58,7 @@ open class SplashActivity : AppCompatActivity() {
// Schedule periodic update checks
Utils.scheduleUpdateCheck()
CheckUpdates.check()
//CheckUpdates.check()
// Setup shortcuts
Shortcuts.setup(this)

View File

@ -10,28 +10,22 @@ import com.skoumal.teanity.util.DiffObservableList
import com.skoumal.teanity.util.KObservableField
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.data.database.RepoDatabaseHelper
import com.topjohnwu.magisk.model.entity.OldModule
import com.topjohnwu.magisk.model.entity.Repo
import com.topjohnwu.magisk.data.repository.ModuleRepository
import com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem
import com.topjohnwu.magisk.model.entity.recycler.RepoRvItem
import com.topjohnwu.magisk.model.entity.recycler.SectionRvItem
import com.topjohnwu.magisk.model.events.InstallModuleEvent
import com.topjohnwu.magisk.model.events.OpenChangelogEvent
import com.topjohnwu.magisk.model.events.OpenFilePickerEvent
import com.topjohnwu.magisk.tasks.UpdateRepos
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.update
import io.reactivex.Single
import io.reactivex.disposables.Disposable
import me.tatarka.bindingcollectionadapter2.OnItemBind
class ModuleViewModel(
private val repoDatabase: RepoDatabaseHelper,
private val resources: Resources
private val resources: Resources,
private val moduleRepo: ModuleRepository
) : MagiskViewModel() {
val query = KObservableField("")
@ -52,36 +46,15 @@ class ModuleViewModel(
queryDisposable?.dispose()
queryDisposable = query()
}
Event.register(this)
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 repoPressed(item: RepoRvItem) = OpenChangelogEvent(item.item).publish()
fun downloadPressed(item: RepoRvItem) = InstallModuleEvent(item.item).publish()
fun refresh() {
state = State.LOADING
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) } }
moduleRepo.fetchModules()
.flattenAsFlowable { it }
.map { RepoRvItem(it) }
.toList()
@ -89,7 +62,13 @@ class ModuleViewModel(
.flatMap { queryRaw() }
.applyViewModel(this)
.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()

View File

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

View File

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

View File

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