more kotlin

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/27/head
androidacy-user 2 years ago
parent 8a54228509
commit bca8507ba5

@ -1,175 +1,173 @@
package com.fox2code.mmm;
package com.fox2code.mmm
import com.fox2code.mmm.utils.io.Files;
import com.fox2code.mmm.utils.io.net.Http;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import timber.log.Timber;
import com.fox2code.mmm.utils.io.Files.Companion.write
import com.fox2code.mmm.utils.io.net.Http.Companion.doHttpGet
import org.json.JSONObject
import timber.log.Timber
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
// See https://docs.github.com/en/rest/reference/repos#releases
public class AppUpdateManager {
public static final int FLAG_COMPAT_LOW_QUALITY = 0x0001;
public static final int FLAG_COMPAT_NO_EXT = 0x0002;
public static final int FLAG_COMPAT_MAGISK_CMD = 0x0004;
public static final int FLAG_COMPAT_NEED_32BIT = 0x0008;
public static final int FLAG_COMPAT_MALWARE = 0x0010;
public static final int FLAG_COMPAT_NO_ANSI = 0x0020;
public static final int FLAG_COMPAT_FORCE_ANSI = 0x0040;
public static final int FLAG_COMPAT_FORCE_HIDE = 0x0080;
public static final int FLAG_COMPAT_MMT_REBORN = 0x0100;
public static final int FLAG_COMPAT_ZIP_WRAPPER = 0x0200;
public static final String RELEASES_API_URL = "https://api.github.com/repos/Androidacy/MagiskModuleManager/releases/latest";
private static final AppUpdateManager INSTANCE = new AppUpdateManager();
private final HashMap<String, Integer> compatDataId = new HashMap<>();
private final Object updateLock = new Object();
private final File compatFile;
private String latestRelease;
private long lastChecked;
@Suppress("unused")
class AppUpdateManager private constructor() {
private val compatDataId = HashMap<String, Int>()
private val updateLock = Any()
private val compatFile: File = File(MainApplication.getINSTANCE().filesDir, "compat.txt")
private var latestRelease: String?
private var lastChecked: Long
private AppUpdateManager() {
this.compatFile = new File(MainApplication.getINSTANCE().getFilesDir(), "compat.txt");
this.latestRelease = MainApplication.getBootSharedPreferences().getString("updater_latest_release", BuildConfig.VERSION_NAME);
this.lastChecked = 0;
if (this.compatFile.isFile()) {
init {
latestRelease = MainApplication.getBootSharedPreferences()
.getString("updater_latest_release", BuildConfig.VERSION_NAME)
lastChecked = 0
if (compatFile.isFile) {
try {
this.parseCompatibilityFlags(new FileInputStream(this.compatFile));
} catch (
IOException ignored) {
parseCompatibilityFlags(FileInputStream(compatFile))
} catch (ignored: IOException) {
}
}
}
public static AppUpdateManager getAppUpdateManager() {
return INSTANCE;
}
public static int getFlagsForModule(String moduleId) {
return INSTANCE.getCompatibilityFlags(moduleId);
}
public static boolean shouldForceHide(String repoId) {
if (BuildConfig.DEBUG || repoId.startsWith("repo_") || repoId.equals("magisk_alt_repo"))
return false;
return !repoId.startsWith("repo_") && (INSTANCE.getCompatibilityFlags(repoId) & FLAG_COMPAT_FORCE_HIDE) != 0;
}
// Return true if should show a notification
public boolean checkUpdate(boolean force) {
if (!BuildConfig.ENABLE_AUTO_UPDATER)
return false;
if (!force && this.peekShouldUpdate())
return true;
long lastChecked = this.lastChecked;
if (lastChecked != 0 &&
// Avoid spam calls by putting a 60 seconds timer
lastChecked < System.currentTimeMillis() - 60000L)
return force && this.peekShouldUpdate();
synchronized (this.updateLock) {
if (lastChecked != this.lastChecked)
return this.peekShouldUpdate();
fun checkUpdate(force: Boolean): Boolean {
if (!BuildConfig.ENABLE_AUTO_UPDATER) return false
if (!force && peekShouldUpdate()) return true
val lastChecked = lastChecked
if (lastChecked != 0L && // Avoid spam calls by putting a 60 seconds timer
lastChecked < System.currentTimeMillis() - 60000L
) return force && peekShouldUpdate()
synchronized(updateLock) {
if (lastChecked != this.lastChecked) return peekShouldUpdate()
try {
JSONObject release = new JSONObject(new String(Http.doHttpGet(RELEASES_API_URL, false), StandardCharsets.UTF_8));
String latestRelease = null;
boolean preRelease = false;
val release =
JSONObject(String(doHttpGet(RELEASES_API_URL, false), StandardCharsets.UTF_8))
var latestRelease: String? = null
var preRelease = false
// get latest_release from tag_name translated to int
if (release.has("tag_name")) {
latestRelease = release.getString("tag_name");
preRelease = release.getBoolean("prerelease");
latestRelease = release.getString("tag_name")
preRelease = release.getBoolean("prerelease")
}
Timber.d("Latest release: %s, isPreRelease: %s", latestRelease, preRelease);
if (latestRelease == null)
return false;
Timber.d("Latest release: %s, isPreRelease: %s", latestRelease, preRelease)
if (latestRelease == null) return false
if (preRelease) {
this.latestRelease = "99999999"; // prevent updating to pre-release
return false;
this.latestRelease = "99999999" // prevent updating to pre-release
return false
}
this.latestRelease = latestRelease;
this.lastChecked = System.currentTimeMillis();
} catch (
Exception ioe) {
Timber.e(ioe);
this.latestRelease = latestRelease
this.lastChecked = System.currentTimeMillis()
} catch (ioe: Exception) {
Timber.e(ioe)
}
}
return this.peekShouldUpdate();
return peekShouldUpdate()
}
public void checkUpdateCompat() {
compatDataId.clear();
fun checkUpdateCompat() {
compatDataId.clear()
try {
Files.write(compatFile, new byte[0]);
} catch (
IOException e) {
Timber.e(e);
write(compatFile, ByteArray(0))
} catch (e: IOException) {
Timber.e(e)
}
// There once lived an implementation that used a GitHub API to get the compatibility flags. It was removed because it was too slow and the API was rate limited.
Timber.w("Remote compatibility data flags are not implemented.");
Timber.w("Remote compatibility data flags are not implemented.")
}
public boolean peekShouldUpdate() {
if (!BuildConfig.ENABLE_AUTO_UPDATER || BuildConfig.DEBUG)
return false;
fun peekShouldUpdate(): Boolean {
if (!BuildConfig.ENABLE_AUTO_UPDATER || BuildConfig.DEBUG) return false
// Convert both BuildConfig.VERSION_NAME and latestRelease to int
int currentVersion = 0, latestVersion = 0;
var currentVersion = 0
var latestVersion = 0
try {
currentVersion = Integer.parseInt(BuildConfig.VERSION_NAME.replaceAll("\\D", ""));
latestVersion = Integer.parseInt(this.latestRelease.replace("v", "").replaceAll("\\D", ""));
} catch (
NumberFormatException ignored) {
currentVersion = BuildConfig.VERSION_NAME.replace("\\D".toRegex(), "").toInt()
latestVersion = latestRelease!!.replace("v", "").replace("\\D".toRegex(), "").toInt()
} catch (ignored: NumberFormatException) {
}
return currentVersion < latestVersion;
return currentVersion < latestVersion
}
public boolean peekHasUpdate() {
if (!BuildConfig.ENABLE_AUTO_UPDATER || BuildConfig.DEBUG)
return false;
return this.peekShouldUpdate();
fun peekHasUpdate(): Boolean {
return if (!BuildConfig.ENABLE_AUTO_UPDATER || BuildConfig.DEBUG) false else peekShouldUpdate()
}
private void parseCompatibilityFlags(InputStream inputStream) throws IOException {
compatDataId.clear();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String line;
while ((line = bufferedReader.readLine()) != null) {
line = line.trim();
if (line.isEmpty() || line.startsWith("#"))
continue;
int i = line.indexOf('/');
if (i == -1)
continue;
int value = 0;
for (String arg : line.substring(i + 1).split(",")) {
switch (arg) {
default -> {
@Throws(IOException::class)
private fun parseCompatibilityFlags(inputStream: InputStream) {
compatDataId.clear()
val bufferedReader = BufferedReader(InputStreamReader(inputStream, StandardCharsets.UTF_8))
var line: String
while (bufferedReader.readLine().also { line = it } != null) {
line = line.trim { it <= ' ' }
if (line.isEmpty() || line.startsWith("#")) continue
val i = line.indexOf('/')
if (i == -1) continue
var value = 0
for (arg in line.substring(i + 1).split(",".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()) {
when (arg) {
"lowQuality" -> value = value or FLAG_COMPAT_LOW_QUALITY
"noExt" -> value = value or FLAG_COMPAT_NO_EXT
"magiskCmd" -> value = value or FLAG_COMPAT_MAGISK_CMD
"need32bit" -> value = value or FLAG_COMPAT_NEED_32BIT
"malware" -> value = value or FLAG_COMPAT_MALWARE
"noANSI" -> value = value or FLAG_COMPAT_NO_ANSI
"forceANSI" -> value = value or FLAG_COMPAT_FORCE_ANSI
"forceHide" -> value = value or FLAG_COMPAT_FORCE_HIDE
"mmtReborn" -> value = value or FLAG_COMPAT_MMT_REBORN
"wrapper" -> value = value or FLAG_COMPAT_ZIP_WRAPPER
else -> {
run {}
value = value or FLAG_COMPAT_LOW_QUALITY
value = value or FLAG_COMPAT_NO_EXT
value = value or FLAG_COMPAT_MAGISK_CMD
value = value or FLAG_COMPAT_NEED_32BIT
value = value or FLAG_COMPAT_MALWARE
value = value or FLAG_COMPAT_NO_ANSI
value = value or FLAG_COMPAT_FORCE_ANSI
value = value or FLAG_COMPAT_FORCE_HIDE
value = value or FLAG_COMPAT_MMT_REBORN
value = value or FLAG_COMPAT_ZIP_WRAPPER
}
case "lowQuality" -> value |= FLAG_COMPAT_LOW_QUALITY;
case "noExt" -> value |= FLAG_COMPAT_NO_EXT;
case "magiskCmd" -> value |= FLAG_COMPAT_MAGISK_CMD;
case "need32bit" -> value |= FLAG_COMPAT_NEED_32BIT;
case "malware" -> value |= FLAG_COMPAT_MALWARE;
case "noANSI" -> value |= FLAG_COMPAT_NO_ANSI;
case "forceANSI" -> value |= FLAG_COMPAT_FORCE_ANSI;
case "forceHide" -> value |= FLAG_COMPAT_FORCE_HIDE;
case "mmtReborn" -> value |= FLAG_COMPAT_MMT_REBORN;
case "wrapper" -> value |= FLAG_COMPAT_ZIP_WRAPPER;
}
}
compatDataId.put(line.substring(0, i), value);
compatDataId[line.substring(0, i)] = value
}
bufferedReader.close();
bufferedReader.close()
}
fun getCompatibilityFlags(moduleId: String): Int {
val compatFlags = compatDataId[moduleId]
return compatFlags ?: 0
}
public int getCompatibilityFlags(String moduleId) {
Integer compatFlags = compatDataId.get(moduleId);
return compatFlags == null ? 0 : compatFlags;
companion object {
const val FLAG_COMPAT_LOW_QUALITY = 0x0001
const val FLAG_COMPAT_NO_EXT = 0x0002
const val FLAG_COMPAT_MAGISK_CMD = 0x0004
const val FLAG_COMPAT_NEED_32BIT = 0x0008
const val FLAG_COMPAT_MALWARE = 0x0010
const val FLAG_COMPAT_NO_ANSI = 0x0020
const val FLAG_COMPAT_FORCE_ANSI = 0x0040
const val FLAG_COMPAT_FORCE_HIDE = 0x0080
const val FLAG_COMPAT_MMT_REBORN = 0x0100
const val FLAG_COMPAT_ZIP_WRAPPER = 0x0200
const val RELEASES_API_URL =
"https://api.github.com/repos/Androidacy/MagiskModuleManager/releases/latest"
val appUpdateManager = AppUpdateManager()
fun getFlagsForModule(moduleId: String): Int {
return appUpdateManager.getCompatibilityFlags(moduleId)
}
@JvmStatic
fun shouldForceHide(repoId: String): Boolean {
return if (BuildConfig.DEBUG || repoId.startsWith("repo_") || repoId == "magisk_alt_repo") false else !repoId.startsWith(
"repo_"
) && appUpdateManager.getCompatibilityFlags(repoId) and FLAG_COMPAT_FORCE_HIDE != 0
}
}
}
}

@ -145,7 +145,7 @@ enum class NotificationType constructor(
false
) {
override fun shouldRemove(): Boolean {
return !AppUpdateManager.getAppUpdateManager().peekShouldUpdate()
return !AppUpdateManager.appUpdateManager.peekShouldUpdate()
}
},
@JvmStatic

@ -177,7 +177,7 @@ class UpdateActivity : FoxActivity() {
progressIndicator.isIndeterminate = true
}
// check for update
val shouldUpdate = AppUpdateManager.getAppUpdateManager().peekShouldUpdate()
val shouldUpdate = AppUpdateManager.appUpdateManager.peekShouldUpdate()
// if shouldUpdate is true, then we have an update
if (shouldUpdate) {
runOnUiThread {

@ -276,7 +276,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
.getBoolean("pref_background_update_check_app", false)
) {
try {
val shouldUpdate = AppUpdateManager.getAppUpdateManager().checkUpdate(true)
val shouldUpdate = AppUpdateManager.appUpdateManager.checkUpdate(true)
if (shouldUpdate) {
Timber.d("Found app update")
postNotificationForAppUpdate(context)

@ -1,128 +1,147 @@
package com.fox2code.mmm.repo;
package com.fox2code.mmm.repo
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.realm.ModuleListCache;
import com.fox2code.mmm.utils.realm.ReposList;
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.utils.io.net.Http.Companion.doHttpGet
import com.fox2code.mmm.utils.realm.ModuleListCache
import com.fox2code.mmm.utils.realm.ReposList
import io.realm.Realm
import io.realm.RealmConfiguration
import org.json.JSONArray
import org.json.JSONObject
import timber.log.Timber
import java.nio.charset.StandardCharsets
import java.util.concurrent.atomic.AtomicBoolean
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmResults;
import timber.log.Timber;
public class RepoUpdater {
public final RepoData repoData;
public byte[] indexRaw;
private List<RepoModule> toUpdate;
private Collection<RepoModule> toApply;
public RepoUpdater(RepoData repoData) {
this.repoData = repoData;
}
public int fetchIndex() {
class RepoUpdater(val repoData: RepoData) {
private var indexRaw: ByteArray? = null
private var toUpdate: List<RepoModule>? = null
private var toApply: Collection<RepoModule>? = null
fun fetchIndex(): Int {
if (!RepoManager.getINSTANCE().hasConnectivity()) {
this.indexRaw = null;
this.toUpdate = Collections.emptyList();
this.toApply = Collections.emptySet();
return 0;
indexRaw = null
toUpdate = emptyList()
toApply = emptySet()
return 0
}
if (!this.repoData.isEnabled()) {
this.indexRaw = null;
this.toUpdate = Collections.emptyList();
this.toApply = Collections.emptySet();
return 0;
if (!repoData.isEnabled) {
indexRaw = null
toUpdate = emptyList()
toApply = emptySet()
return 0
}
// if we shouldn't update, get the values from the ModuleListCache realm
if (!this.repoData.shouldUpdate() && Objects.equals(this.repoData.id, "androidacy_repo")) { // for now, only enable cache reading for androidacy repo, until we handle storing module prop file values in cache
Timber.d("Fetching index from cache for %s", this.repoData.id);
File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + this.repoData.id);
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").encryptionKey(MainApplication.getINSTANCE().getKey()).schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build();
Realm realm = Realm.getInstance(realmConfiguration);
RealmResults<ModuleListCache> results = realm.where(ModuleListCache.class).equalTo("repoId", this.repoData.id).findAll();
if (!repoData.shouldUpdate() && repoData.id == "androidacy_repo") { // for now, only enable cache reading for androidacy repo, until we handle storing module prop file values in cache
Timber.d("Fetching index from cache for %s", repoData.id)
val cacheRoot =
MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + repoData.id)
val realmConfiguration = RealmConfiguration.Builder().name("ModuleListCache.realm")
.encryptionKey(MainApplication.getINSTANCE().key).schemaVersion(1)
.deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true)
.allowQueriesOnUiThread(true).directory(cacheRoot).build()
val realm = Realm.getInstance(realmConfiguration)
val results = realm.where(
ModuleListCache::class.java
).equalTo("repoId", repoData.id).findAll()
// repos-list realm
RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").encryptionKey(MainApplication.getINSTANCE().getKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build();
Realm realm2 = Realm.getInstance(realmConfiguration2);
this.toUpdate = Collections.emptyList();
this.toApply = new HashSet<>();
for (ModuleListCache moduleListCache : results) {
this.toApply.add(new RepoModule(this.repoData, moduleListCache.getCodename(), moduleListCache.getName(), moduleListCache.getDescription(), moduleListCache.getAuthor(), moduleListCache.getDonate(), moduleListCache.getConfig(), moduleListCache.getSupport(), moduleListCache.getVersion(), moduleListCache.getVersionCode()));
val realmConfiguration2 = RealmConfiguration.Builder().name("ReposList.realm")
.encryptionKey(MainApplication.getINSTANCE().key).allowQueriesOnUiThread(true)
.allowWritesOnUiThread(true)
.directory(MainApplication.getINSTANCE().getDataDirWithPath("realms"))
.schemaVersion(1).build()
val realm2 = Realm.getInstance(realmConfiguration2)
toUpdate = emptyList()
toApply = HashSet()
for (moduleListCache in results) {
(toApply as HashSet<RepoModule>).add(
RepoModule(
repoData,
moduleListCache.codename,
moduleListCache.name,
moduleListCache.description,
moduleListCache.author,
moduleListCache.donate,
moduleListCache.config,
moduleListCache.support,
moduleListCache.version,
moduleListCache.versionCode
)
)
}
Timber.d("Fetched %d modules from cache for %s, from %s records", this.toApply.size(), this.repoData.id, results.size());
Timber.d(
"Fetched %d modules from cache for %s, from %s records",
(toApply as HashSet<RepoModule>).size,
repoData.id,
results.size
)
// apply the toApply list to the toUpdate list
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("modules", new JSONArray(results.asJSON()));
this.toUpdate = this.repoData.populate(jsonObject);
} catch (Exception e) {
Timber.e(e);
val jsonObject = JSONObject()
jsonObject.put("modules", JSONArray(results.asJSON()))
toUpdate = repoData.populate(jsonObject)
} catch (e: Exception) {
Timber.e(e)
}
// close realm
realm.close();
realm2.close();
realm.close()
realm2.close()
// Since we reuse instances this should work
this.toApply = new HashSet<>(this.repoData.moduleHashMap.values());
this.toApply.removeAll(this.toUpdate);
toApply = HashSet(repoData.moduleHashMap.values)
(toApply as HashSet<RepoModule>).removeAll(toUpdate!!.toSet())
// Return repo to update
return this.toUpdate.size();
return toUpdate!!.size
}
try {
if (!this.repoData.prepare()) {
this.indexRaw = null;
this.toUpdate = Collections.emptyList();
this.toApply = this.repoData.moduleHashMap.values();
return 0;
return try {
if (!repoData.prepare()) {
indexRaw = null
toUpdate = emptyList()
toApply = repoData.moduleHashMap.values
return 0
}
this.indexRaw = Http.doHttpGet(this.repoData.getUrl(), false);
this.toUpdate = this.repoData.populate(new JSONObject(new String(this.indexRaw, StandardCharsets.UTF_8)));
indexRaw = doHttpGet(repoData.getUrl(), false)
toUpdate = repoData.populate(JSONObject(String(indexRaw!!, StandardCharsets.UTF_8)))
// Since we reuse instances this should work
this.toApply = new HashSet<>(this.repoData.moduleHashMap.values());
this.toApply.removeAll(this.toUpdate);
toApply = HashSet(repoData.moduleHashMap.values)
(toUpdate as MutableList<RepoModule>?)?.let {
(toApply as HashSet<RepoModule>).removeAll(
it.toSet()
)
}
// Return repo to update
return this.toUpdate.size();
} catch (
Exception e) {
Timber.e(e);
this.indexRaw = null;
this.toUpdate = Collections.emptyList();
this.toApply = Collections.emptySet();
return 0;
(toUpdate as MutableList<RepoModule>?)!!.size
} catch (e: Exception) {
Timber.e(e)
indexRaw = null
toUpdate = emptyList()
toApply = emptySet()
0
}
}
public List<RepoModule> toUpdate() {
return this.toUpdate;
fun toUpdate(): List<RepoModule>? {
return toUpdate
}
public Collection<RepoModule> toApply() {
return this.toApply;
fun toApply(): Collection<RepoModule>? {
return toApply
}
public boolean finish() {
var success = new AtomicBoolean(false);
fun finish(): Boolean {
val success = AtomicBoolean(false)
// If repo is not enabled we don't need to do anything, just return true
if (!this.repoData.isEnabled()) {
return true;
if (!repoData.isEnabled) {
return true
}
if (this.indexRaw != null) {
if (indexRaw != null) {
try {
// iterate over modules, using this.supportedProperties as a template to attempt to get each property from the module. everything that is not null is added to the module
// use realm to insert to
// props avail:
File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + this.repoData.id);
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").encryptionKey(MainApplication.getINSTANCE().getKey()).schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build();
val cacheRoot =
MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + repoData.id)
val realmConfiguration = RealmConfiguration.Builder().name("ModuleListCache.realm")
.encryptionKey(MainApplication.getINSTANCE().key).schemaVersion(1)
.deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true)
.allowQueriesOnUiThread(true).directory(cacheRoot).build()
// array with module info default values
// supported properties for a module
//id=<string>
@ -147,146 +166,134 @@ public class RepoUpdater {
//
// all except first six can be null
// this.indexRaw is the raw index file (json)
JSONObject modules = new JSONObject(new String(this.indexRaw, StandardCharsets.UTF_8));
JSONArray modulesArray;
val modules = JSONObject(String(indexRaw!!, StandardCharsets.UTF_8))
// androidacy repo uses "data" key, others should use "modules" key. Both are JSONArrays
if (this.repoData.getName().equals("Androidacy Modules Repo")) {
val modulesArray: JSONArray = if (repoData.name == "Androidacy Modules Repo") {
// get modules from "data" key. This is a JSONArray so we need to convert it to a JSONObject
modulesArray = modules.getJSONArray("data");
modules.getJSONArray("data")
} else {
// get modules from "modules" key. This is a JSONArray so we need to convert it to a JSONObject
modulesArray = modules.getJSONArray("modules");
modules.getJSONArray("modules")
}
Realm realm = Realm.getInstance(realmConfiguration);
val realm = Realm.getInstance(realmConfiguration)
// drop old data
if (realm.isInTransaction()) {
realm.commitTransaction();
if (realm.isInTransaction) {
realm.commitTransaction()
}
realm.beginTransaction();
realm.where(ModuleListCache.class).equalTo("repoId", this.repoData.id).findAll().deleteAllFromRealm();
realm.commitTransaction();
realm.beginTransaction()
realm.where(ModuleListCache::class.java).equalTo("repoId", repoData.id).findAll()
.deleteAllFromRealm()
realm.commitTransaction()
// iterate over modules. pls don't hate me for this, its ugly but it works
for (int n = 0; n < modulesArray.length(); n++) {
for (n in 0 until modulesArray.length()) {
// get module
JSONObject module = modulesArray.getJSONObject(n);
val module = modulesArray.getJSONObject(n)
try {
// get module id
// if codename is present, prefer that over id
String id;
if (module.has("codename") && !module.getString("codename").equals("")) {
id = module.getString("codename");
val id: String? = if (module.has("codename") && module.getString("codename") != "") {
module.getString("codename")
} else {
id = module.getString("id");
module.getString("id")
}
// get module name
String name = module.getString("name");
val name = module.getString("name")
// get module version
String version = module.getString("version");
val version = module.getString("version")
// get module version code
int versionCode = module.getInt("versionCode");
val versionCode = module.getInt("versionCode")
// get module author
String author = module.getString("author");
val author = module.getString("author")
// get module description
String description = module.getString("description");
val description = module.getString("description")
// get module min api
String minApi;
if (module.has("minApi") && !module.getString("minApi").equals("")) {
minApi = module.getString("minApi");
val minApi: String = if (module.has("minApi") && module.getString("minApi") != "") {
module.getString("minApi")
} else {
minApi = "0";
"0"
}
// coerce min api to int
int minApiInt = Integer.parseInt(minApi);
val minApiInt = minApi.toInt()
// get module max api and set to 0 if it's "" or null
String maxApi;
if (module.has("maxApi") && !module.getString("maxApi").equals("")) {
maxApi = module.getString("maxApi");
val maxApi: String = if (module.has("maxApi") && module.getString("maxApi") != "") {
module.getString("maxApi")
} else {
maxApi = "0";
"0"
}
// coerce max api to int
int maxApiInt = Integer.parseInt(maxApi);
val maxApiInt = maxApi.toInt()
// get module min magisk
String minMagisk;
if (module.has("minMagisk") && !module.getString("minMagisk").equals("")) {
minMagisk = module.getString("minMagisk");
} else {
minMagisk = "0";
}
val minMagisk: String = if (module.has("minMagisk") && module.getString("minMagisk") != "") {
module.getString("minMagisk")
} else {
"0"
}
// coerce min magisk to int
int minMagiskInt = Integer.parseInt(minMagisk);
val minMagiskInt = minMagisk.toInt()
// get module need ramdisk
boolean needRamdisk;
if (module.has("needRamdisk")) {
needRamdisk = module.getBoolean("needRamdisk");
val needRamdisk: Boolean = if (module.has("needRamdisk")) {
module.getBoolean("needRamdisk")
} else {
needRamdisk = false;
false
}
// get module support
String support;
if (module.has("support")) {
support = module.getString("support");
val support: String? = if (module.has("support")) {
module.getString("support")
} else {
support = "";
""
}
// get module donate
String donate;
if (module.has("donate")) {
donate = module.getString("donate");
val donate: String? = if (module.has("donate")) {
module.getString("donate")
} else {
donate = "";
""
}
// get module config
String config;
if (module.has("config")) {
config = module.getString("config");
val config: String? = if (module.has("config")) {
module.getString("config")
} else {
config = "";
""
}
// get module change boot
boolean changeBoot;
if (module.has("changeBoot")) {
changeBoot = module.getBoolean("changeBoot");
val changeBoot: Boolean = if (module.has("changeBoot")) {
module.getBoolean("changeBoot")
} else {
changeBoot = false;
false
}
// get module mmt reborn
boolean mmtReborn;
if (module.has("mmtReborn")) {
mmtReborn = module.getBoolean("mmtReborn");
val mmtReborn: Boolean = if (module.has("mmtReborn")) {
module.getBoolean("mmtReborn")
} else {
mmtReborn = false;
false
}
// try to get updated_at or lastUpdate value for lastUpdate
int lastUpdate;
if (module.has("updated_at")) {
lastUpdate = module.getInt("updated_at");
val lastUpdate: Int = if (module.has("updated_at")) {
module.getInt("updated_at")
} else if (module.has("lastUpdate")) {
lastUpdate = module.getInt("lastUpdate");
module.getInt("lastUpdate")
} else {
lastUpdate = 0;
0
}
// now downloads or stars
int downloads;
if (module.has("downloads")) {
downloads = module.getInt("downloads");
val downloads: Int = if (module.has("downloads")) {
module.getInt("downloads")
} else if (module.has("stars")) {
downloads = module.getInt("stars");
module.getInt("stars")
} else {
downloads = 0;
0
}
// get module repo id
String repoId = this.repoData.id;
val repoId = repoData.id
// get module installed
boolean installed = false;
val installed = false
// get module installed version code
int installedVersionCode = 0;
val installedVersionCode = 0
// get safe property. for now, only supported by androidacy repo and they use "vt_status" key
boolean safe = false;
if (this.repoData.getName().equals("Androidacy Modules Repo")) {
var safe = false
if (repoData.name == "Androidacy Modules Repo") {
if (module.has("vt_status")) {
if (module.getString("vt_status").equals("Clean")) {
safe = true;
if (module.getString("vt_status") == "Clean") {
safe = true
}
}
}
@ -295,70 +302,79 @@ public class RepoUpdater {
// then insert to realm
// then commit
// then close
if (realm.isInTransaction()) {
realm.cancelTransaction();
if (realm.isInTransaction) {
realm.cancelTransaction()
}
// create a realm object and insert or update it
// add everything to the realm object
if (realm.isInTransaction()) {
realm.commitTransaction();
if (realm.isInTransaction) {
realm.commitTransaction()
}
realm.beginTransaction();
ModuleListCache moduleListCache = realm.createObject(ModuleListCache.class, id);
moduleListCache.setName(name);
moduleListCache.setVersion(version);
moduleListCache.setVersionCode(versionCode);
moduleListCache.setAuthor(author);
moduleListCache.setDescription(description);
moduleListCache.setMinApi(minApiInt);
moduleListCache.setMaxApi(maxApiInt);
moduleListCache.setMinMagisk(minMagiskInt);
moduleListCache.setNeedRamdisk(needRamdisk);
moduleListCache.setSupport(support);
moduleListCache.setDonate(donate);
moduleListCache.setConfig(config);
moduleListCache.setChangeBoot(changeBoot);
moduleListCache.setMmtReborn(mmtReborn);
moduleListCache.setRepoId(repoId);
moduleListCache.setInstalled(installed);
moduleListCache.setInstalledVersionCode(installedVersionCode);
moduleListCache.setSafe(safe);
moduleListCache.setLastUpdate(lastUpdate);
moduleListCache.setStats(downloads);
realm.copyToRealmOrUpdate(moduleListCache);
realm.commitTransaction();
} catch (
Exception ignored) {
realm.beginTransaction()
val moduleListCache = realm.createObject(
ModuleListCache::class.java, id
)
moduleListCache.name = name
moduleListCache.version = version
moduleListCache.versionCode = versionCode
moduleListCache.author = author
moduleListCache.description = description
moduleListCache.minApi = minApiInt
moduleListCache.maxApi = maxApiInt
moduleListCache.minMagisk = minMagiskInt
moduleListCache.isNeedRamdisk = needRamdisk
moduleListCache.support = support
moduleListCache.donate = donate
moduleListCache.config = config
moduleListCache.isChangeBoot = changeBoot
moduleListCache.isMmtReborn = mmtReborn
moduleListCache.repoId = repoId
moduleListCache.isInstalled = installed
moduleListCache.installedVersionCode = installedVersionCode
moduleListCache.isSafe = safe
moduleListCache.lastUpdate = lastUpdate
moduleListCache.stats = downloads
realm.copyToRealmOrUpdate(moduleListCache)
realm.commitTransaction()
} catch (ignored: Exception) {
}
}
realm.close();
} catch (
Exception ignored) {
realm.close()
} catch (ignored: Exception) {
}
this.indexRaw = null;
RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").encryptionKey(MainApplication.getINSTANCE().getKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build();
Realm realm2 = Realm.getInstance(realmConfiguration2);
if (realm2.isInTransaction()) {
realm2.cancelTransaction();
indexRaw = null
val realmConfiguration2 = RealmConfiguration.Builder().name("ReposList.realm")
.encryptionKey(MainApplication.getINSTANCE().key).allowQueriesOnUiThread(true)
.allowWritesOnUiThread(true)
.directory(MainApplication.getINSTANCE().getDataDirWithPath("realms"))
.schemaVersion(1).build()
val realm2 = Realm.getInstance(realmConfiguration2)
if (realm2.isInTransaction) {
realm2.cancelTransaction()
}
// set lastUpdate
realm2.executeTransaction(r -> {
ReposList repoListCache = r.where(ReposList.class).equalTo("id", this.repoData.id).findFirst();
realm2.executeTransaction { r: Realm ->
val repoListCache =
r.where(ReposList::class.java).equalTo("id", repoData.id).findFirst()
if (repoListCache != null) {
success.set(true);
success.set(true)
// get unix timestamp of current time
int currentTime = (int) (System.currentTimeMillis() / 1000);
Timber.d("Updating lastUpdate for repo %s to %s which is %s seconds ago", this.repoData.id, currentTime, (currentTime - repoListCache.getLastUpdate()));
repoListCache.setLastUpdate(currentTime);
val currentTime = (System.currentTimeMillis() / 1000).toInt()
Timber.d(
"Updating lastUpdate for repo %s to %s which is %s seconds ago",
repoData.id,
currentTime,
currentTime - repoListCache.lastUpdate
)
repoListCache.lastUpdate = currentTime
} else {
Timber.w("Failed to update lastUpdate for repo %s", this.repoData.id);
Timber.w("Failed to update lastUpdate for repo %s", repoData.id)
}
});
realm2.close();
}
realm2.close()
} else {
success.set(true); // assume we're reading from cache. this may be unsafe but it's better than nothing
success.set(true) // assume we're reading from cache. this may be unsafe but it's better than nothing
}
return success.get();
return success.get()
}
}
}
Loading…
Cancel
Save