package com.fox2code.mmm.repo; import com.fox2code.mmm.utils.io.Http; import com.fox2code.mmm.utils.realm.ModuleListCache; 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 io.realm.Realm; import io.realm.RealmConfiguration; import timber.log.Timber; public class RepoUpdater { public final RepoData repoData; public byte[] indexRaw; private List toUpdate; private Collection toApply; public RepoUpdater(RepoData repoData) { this.repoData = repoData; } public int fetchIndex() { if (!RepoManager.getINSTANCE().hasConnectivity()) { this.indexRaw = null; this.toUpdate = Collections.emptyList(); this.toApply = Collections.emptySet(); return 0; } if (!this.repoData.isEnabled()) { this.indexRaw = null; this.toUpdate = Collections.emptyList(); this.toApply = Collections.emptySet(); return 0; } try { if (!this.repoData.prepare()) { this.indexRaw = null; this.toUpdate = Collections.emptyList(); this.toApply = this.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))); // Since we reuse instances this should work this.toApply = new HashSet<>(this.repoData.moduleHashMap.values()); this.toApply.removeAll(this.toUpdate); // 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; } } public List toUpdate() { return this.toUpdate; } public Collection toApply() { return this.toApply; } public boolean finish() { boolean success = this.indexRaw != null; // If repo is not enabled we don't need to do anything, just return true if (!this.repoData.isEnabled()) { return true; } if (this.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 = this.repoData.cacheRoot; RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); // array with module info default values // supported properties for a module //id= //name= //version= //versionCode= //author= //description= //minApi= //maxApi= //minMagisk= //needRamdisk= //support= //donate= //config= //changeBoot= //mmtReborn= // extra properties only useful for the database //repoId= //installed= //installedVersionCode= (only if installed) // // 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; // androidacy repo uses "data" key, others should use "modules" key. Both are JSONArrays if (this.repoData.getName().equals("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"); } else { // get modules from "modules" key. This is a JSONArray so we need to convert it to a JSONObject modulesArray = modules.getJSONArray("modules"); } // iterate over modules. pls dont hate me for this, its ugly but it works for (int n = 0; n < modulesArray.length(); n++) { // get module JSONObject module = modulesArray.getJSONObject(n); try { // get module id String id = module.getString("id"); // get module name String name = module.getString("name"); // get module version String version = module.getString("version"); // get module version code int versionCode = module.getInt("versionCode"); // get module author String author = module.getString("author"); // get module description String description = module.getString("description"); // get module min api String minApi; if (module.has("minApi") && !module.getString("minApi").equals("")) { minApi = module.getString("minApi"); } else { minApi = "0"; } // coerce min api to int int minApiInt = Integer.parseInt(minApi); // 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"); } else { maxApi = "0"; } // coerce max api to int int maxApiInt = Integer.parseInt(maxApi); // get module min magisk String minMagisk; if (module.has("minMagisk") && !module.getString("minMagisk").equals("")) { minMagisk = module.getString("minMagisk"); } else { minMagisk = "0"; } // coerce min magisk to int int minMagiskInt = Integer.parseInt(minMagisk); // get module need ramdisk boolean needRamdisk; if (module.has("needRamdisk")) { needRamdisk = module.getBoolean("needRamdisk"); } else { needRamdisk = false; } // get module support String support; if (module.has("support")) { support = module.getString("support"); } else { support = ""; } // get module donate String donate; if (module.has("donate")) { donate = module.getString("donate"); } else { donate = ""; } // get module config String config; if (module.has("config")) { config = module.getString("config"); } else { config = ""; } // get module change boot boolean changeBoot; if (module.has("changeBoot")) { changeBoot = module.getBoolean("changeBoot"); } else { changeBoot = false; } // get module mmt reborn boolean mmtReborn; if (module.has("mmtReborn")) { mmtReborn = module.getBoolean("mmtReborn"); } else { mmtReborn = false; } // get module repo id String repoId = this.repoData.id; // get module installed boolean installed = false; // get module installed version code int installedVersionCode = 0; // insert module to realm // first create a collection of all the properties // then insert to realm // then commit // then close Realm realm = Realm.getInstance(realmConfiguration); if (realm.isInTransaction()) { realm.cancelTransaction(); } realm.executeTransaction(r -> { // create the object // if it already exists, it will be updated // create a new module ModuleListCache moduleListCache = r.createObject(ModuleListCache.class, id); // set module name moduleListCache.setName(name); // set module version moduleListCache.setVersion(version); // set module version code moduleListCache.setVersionCode(versionCode); // set module author moduleListCache.setAuthor(author); // set module description moduleListCache.setDescription(description); // set module min api moduleListCache.setMinApi(minApiInt); // set module max api moduleListCache.setMaxApi(maxApiInt); // set module min magisk moduleListCache.setMinMagisk(minMagiskInt); // set module need ramdisk moduleListCache.setNeedRamdisk(needRamdisk); // set module support moduleListCache.setSupport(support); // set module donate moduleListCache.setDonate(donate); // set module config moduleListCache.setConfig(config); // set module change boot moduleListCache.setChangeBoot(changeBoot); // set module mmt reborn moduleListCache.setMmtReborn(mmtReborn); // set module repo id moduleListCache.setRepoId(repoId); // set module installed moduleListCache.setInstalled(installed); // set module installed version code moduleListCache.setInstalledVersionCode(installedVersionCode); }); realm.close(); } catch ( Exception e) { e.printStackTrace(); Timber.w("Failed to get module info from module " + module + " in repo " + this.repoData.id + " with error " + e.getMessage()); } } } catch ( Exception e) { e.printStackTrace(); } this.indexRaw = null; } this.toUpdate = null; this.toApply = null; return success; } }