Support unlimited amount of repos
This commit is contained in:
parent
7fec9a3cc6
commit
6a9e39c470
@ -7,7 +7,6 @@ import android.preference.PreferenceCategory;
|
|||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
import android.preference.SwitchPreference;
|
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
@ -12,14 +12,15 @@ import com.topjohnwu.magisk.utils.ValueSortedMap;
|
|||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -27,94 +28,156 @@ public class LoadRepos extends ParallelTask<Void, Void, Void> {
|
|||||||
|
|
||||||
public static final String ETAG_KEY = "ETag";
|
public static final String ETAG_KEY = "ETag";
|
||||||
|
|
||||||
private static final String REPO_URL = "https://api.github.com/orgs/Magisk-Modules-Repo/repos";
|
private static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&page=%d";
|
||||||
|
private static final String IF_NONE_MATCH = "If-None-Match";
|
||||||
|
private static final String LINK_KEY = "Link";
|
||||||
|
|
||||||
private String prefsPath;
|
private static final int CHECK_ETAG = 0;
|
||||||
|
private static final int LOAD_NEXT = 1;
|
||||||
|
private static final int LOAD_PREV = 2;
|
||||||
|
|
||||||
|
private List<String> etags;
|
||||||
|
private ValueSortedMap<String, Repo> cached, fetched;
|
||||||
|
private RepoDatabaseHelper repoDB;
|
||||||
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
public LoadRepos(Activity context) {
|
public LoadRepos(Activity context) {
|
||||||
super(context);
|
super(context);
|
||||||
prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs";
|
prefs = magiskManager.prefs;
|
||||||
|
String prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs";
|
||||||
|
repoDB = new RepoDatabaseHelper(magiskManager);
|
||||||
|
// Legacy data cleanup
|
||||||
|
File old = new File(prefsPath, "RepoMap.xml");
|
||||||
|
if (old.exists() || !prefs.getString("repomap", "empty").equals("empty")) {
|
||||||
|
old.delete();
|
||||||
|
prefs.edit().remove("version").remove("repomap").remove(ETAG_KEY).apply();
|
||||||
|
repoDB.clearRepo();
|
||||||
|
}
|
||||||
|
etags = new ArrayList<>(
|
||||||
|
Arrays.asList(magiskManager.prefs.getString(ETAG_KEY, "").split(",")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadJSON(String jsonString) throws Exception {
|
||||||
|
JSONArray jsonArray = new JSONArray(jsonString);
|
||||||
|
|
||||||
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
|
JSONObject jsonobject = jsonArray.getJSONObject(i);
|
||||||
|
String id = jsonobject.getString("description");
|
||||||
|
String name = jsonobject.getString("name");
|
||||||
|
String lastUpdate = jsonobject.getString("pushed_at");
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||||
|
Date updatedDate = format.parse(lastUpdate);
|
||||||
|
Repo repo = cached.get(id);
|
||||||
|
try {
|
||||||
|
if (repo == null) {
|
||||||
|
Logger.dev("LoadRepos: Create new repo " + id);
|
||||||
|
repo = new Repo(name, updatedDate);
|
||||||
|
} else {
|
||||||
|
// Popout from cached
|
||||||
|
cached.remove(id);
|
||||||
|
repo.update(updatedDate);
|
||||||
|
}
|
||||||
|
if (repo.getId() != null) {
|
||||||
|
fetched.put(id, repo);
|
||||||
|
}
|
||||||
|
} catch (BaseModule.CacheModException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean loadPage(int page, String url, int mode) {
|
||||||
|
Logger.dev("LoadRepos: Loading page: " + (page + 1));
|
||||||
|
Map<String, String> header = new HashMap<>();
|
||||||
|
if (mode == CHECK_ETAG && page < etags.size() && !TextUtils.isEmpty(etags.get(page))) {
|
||||||
|
Logger.dev("ETAG: " + etags.get(page));
|
||||||
|
header.put(IF_NONE_MATCH, etags.get(page));
|
||||||
|
}
|
||||||
|
if (url == null) {
|
||||||
|
url = String.format(Locale.US, REPO_URL, page + 1);
|
||||||
|
}
|
||||||
|
String jsonString = WebService.request(url, WebService.GET, header, true);
|
||||||
|
if (TextUtils.isEmpty(jsonString)) {
|
||||||
|
// At least check the pages we know
|
||||||
|
return page + 1 < etags.size() && loadPage(page + 1, null, CHECK_ETAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The request succeed, parse the new stuffs
|
||||||
|
try {
|
||||||
|
loadJSON(jsonString);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the ETAG
|
||||||
|
String newEtag = header.get(ETAG_KEY);
|
||||||
|
newEtag = newEtag.substring(newEtag.indexOf('\"'), newEtag.lastIndexOf('\"') + 1);
|
||||||
|
Logger.dev("New ETAG: " + newEtag);
|
||||||
|
if (page < etags.size()) {
|
||||||
|
etags.set(page, newEtag);
|
||||||
|
} else {
|
||||||
|
etags.add(newEtag);
|
||||||
|
}
|
||||||
|
|
||||||
|
String links = header.get(LINK_KEY);
|
||||||
|
if (links != null) {
|
||||||
|
if (mode == CHECK_ETAG || mode == LOAD_NEXT) {
|
||||||
|
// Try to check next page URL
|
||||||
|
url = null;
|
||||||
|
for (String s : links.split(", ")) {
|
||||||
|
if (s.contains("next")) {
|
||||||
|
url = s.substring(s.indexOf("<") + 1, s.indexOf(">; "));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (url != null) {
|
||||||
|
loadPage(page + 1, url, LOAD_NEXT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == CHECK_ETAG || mode == LOAD_PREV) {
|
||||||
|
// Try to check prev page URL
|
||||||
|
url = null;
|
||||||
|
for (String s : links.split(", ")) {
|
||||||
|
if (s.contains("prev")) {
|
||||||
|
url = s.substring(s.indexOf("<") + 1, s.indexOf(">; "));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (url != null) {
|
||||||
|
loadPage(page - 1, url, LOAD_PREV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
Logger.dev("LoadRepos: Loading repos");
|
Logger.dev("LoadRepos: Loading repos");
|
||||||
|
|
||||||
SharedPreferences prefs = magiskManager.prefs;
|
cached = repoDB.getRepoMap(false);
|
||||||
|
fetched = new ValueSortedMap<>();
|
||||||
|
|
||||||
RepoDatabaseHelper dbHelper = new RepoDatabaseHelper(magiskManager);
|
if (!loadPage(0, null, CHECK_ETAG)) {
|
||||||
|
magiskManager.repoMap = repoDB.getRepoMap();
|
||||||
// Legacy data cleanup
|
Logger.dev("LoadRepos: No updates, use DB");
|
||||||
File old = new File(prefsPath, "RepoMap.xml");
|
return null;
|
||||||
if (old.exists() || !prefs.getString("repomap", "empty").equals("empty")) {
|
|
||||||
old.delete();
|
|
||||||
prefs.edit().remove("version").remove("repomap").remove(ETAG_KEY).apply();
|
|
||||||
dbHelper.clearRepo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> header = new HashMap<>();
|
repoDB.addRepoMap(fetched);
|
||||||
// Get cached ETag to add in the request header
|
repoDB.removeRepo(cached);
|
||||||
String etag = prefs.getString(ETAG_KEY, "");
|
|
||||||
header.put("If-None-Match", etag);
|
|
||||||
|
|
||||||
// Make a request to main URL for repo info
|
// Update ETag
|
||||||
String jsonString = WebService.request(REPO_URL, WebService.GET, null, header, false);
|
StringBuilder etagBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < etags.size(); ++i) {
|
||||||
ValueSortedMap<String, Repo> cached = dbHelper.getRepoMap(false), fetched = new ValueSortedMap<>();
|
if (i != 0) etagBuilder.append(",");
|
||||||
|
etagBuilder.append(etags.get(i));
|
||||||
if (!TextUtils.isEmpty(jsonString)) {
|
|
||||||
try {
|
|
||||||
JSONArray jsonArray = new JSONArray(jsonString);
|
|
||||||
// If it gets to this point, the response is valid, update ETag
|
|
||||||
etag = WebService.getLastResponseHeader().get(ETAG_KEY).get(0);
|
|
||||||
// Maybe bug in Android build tools, sometimes the ETag has crap in it...
|
|
||||||
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
|
||||||
|
|
||||||
// Update repo info
|
|
||||||
for (int i = 0; i < jsonArray.length(); i++) {
|
|
||||||
JSONObject jsonobject = jsonArray.getJSONObject(i);
|
|
||||||
String id = jsonobject.getString("description");
|
|
||||||
String name = jsonobject.getString("name");
|
|
||||||
String lastUpdate = jsonobject.getString("pushed_at");
|
|
||||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
|
||||||
Date updatedDate;
|
|
||||||
try {
|
|
||||||
updatedDate = format.parse(lastUpdate);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Repo repo = cached.get(id);
|
|
||||||
try {
|
|
||||||
if (repo == null) {
|
|
||||||
Logger.dev("LoadRepos: Create new repo " + id);
|
|
||||||
repo = new Repo(name, updatedDate);
|
|
||||||
} else {
|
|
||||||
// Popout from cached
|
|
||||||
cached.remove(id);
|
|
||||||
Logger.dev("LoadRepos: Update cached repo " + id);
|
|
||||||
repo.update(updatedDate);
|
|
||||||
}
|
|
||||||
if (repo.getId() != null) {
|
|
||||||
fetched.put(id, repo);
|
|
||||||
}
|
|
||||||
} catch (BaseModule.CacheModException ignored) {}
|
|
||||||
|
|
||||||
// Update the database
|
|
||||||
dbHelper.addRepoMap(fetched);
|
|
||||||
// The leftover cached are those removed remote, cleanup db
|
|
||||||
dbHelper.removeRepo(cached);
|
|
||||||
// Update ETag
|
|
||||||
prefs.edit().putString(ETAG_KEY, etag).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
prefs.edit().putString(ETAG_KEY, etagBuilder.toString()).apply();
|
||||||
|
|
||||||
magiskManager.repoMap = dbHelper.getRepoMap();
|
magiskManager.repoMap = repoDB.getRepoMap();
|
||||||
|
Logger.dev("LoadRepos: Done");
|
||||||
Logger.dev("LoadRepos: Repo load done");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
private static final int DATABASE_VER = 2;
|
private static final int DATABASE_VER = 2;
|
||||||
private static final String TABLE_NAME = "repos";
|
private static final String TABLE_NAME = "repos";
|
||||||
|
private static final int MIN_TEMPLATE_VER = 3;
|
||||||
|
|
||||||
public RepoDatabaseHelper(Context context) {
|
public RepoDatabaseHelper(Context context) {
|
||||||
super(context, "repo.db", null, DATABASE_VER);
|
super(context, "repo.db", null, DATABASE_VER);
|
||||||
@ -45,6 +46,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
SQLiteDatabase db = getWritableDatabase();
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
Collection<Repo> list = map.values();
|
Collection<Repo> list = map.values();
|
||||||
for (Repo repo : list) {
|
for (Repo repo : list) {
|
||||||
|
Logger.dev("Add to DB: " + repo.getId());
|
||||||
db.replace(TABLE_NAME, null, repo.getContentValues());
|
db.replace(TABLE_NAME, null, repo.getContentValues());
|
||||||
}
|
}
|
||||||
db.close();
|
db.close();
|
||||||
@ -60,6 +62,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
SQLiteDatabase db = getWritableDatabase();
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
Collection<Repo> list = map.values();
|
Collection<Repo> list = map.values();
|
||||||
for (Repo repo : list) {
|
for (Repo repo : list) {
|
||||||
|
Logger.dev("Remove from DB: " + repo.getId());
|
||||||
db.delete(TABLE_NAME, "id=?", new String[] { repo.getId() });
|
db.delete(TABLE_NAME, "id=?", new String[] { repo.getId() });
|
||||||
}
|
}
|
||||||
db.close();
|
db.close();
|
||||||
@ -76,12 +79,12 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
try (Cursor c = db.query(TABLE_NAME, null, null, null, null, null, null)) {
|
try (Cursor c = db.query(TABLE_NAME, null, null, null, null, null, null)) {
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
repo = new Repo(c);
|
repo = new Repo(c);
|
||||||
Logger.dev("Load from cache: " + repo.getId());
|
if (repo.getTemplateVersion() < MIN_TEMPLATE_VER && filtered) {
|
||||||
if (repo.getTemplateVersion() < 3 && filtered) {
|
|
||||||
Logger.dev("Outdated repo: " + repo.getId());
|
Logger.dev("Outdated repo: " + repo.getId());
|
||||||
continue;
|
} else {
|
||||||
|
// Logger.dev("Load from DB: " + repo.getId());
|
||||||
|
ret.put(repo.getId(), repo);
|
||||||
}
|
}
|
||||||
ret.put(repo.getId(), repo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db.close();
|
db.close();
|
||||||
|
@ -29,14 +29,13 @@ public class Repo extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update() throws CacheModException {
|
public void update() throws CacheModException {
|
||||||
Logger.dev("Repo: Re-fetch prop");
|
String props = WebService.request(getManifestUrl(), WebService.GET);
|
||||||
String props = WebService.request(getManifestUrl(), WebService.GET, true);
|
|
||||||
String lines[] = props.split("\\n");
|
String lines[] = props.split("\\n");
|
||||||
parseProps(lines);
|
parseProps(lines);
|
||||||
|
Logger.dev("Repo: Fetching prop: " + getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(Date lastUpdate) throws CacheModException {
|
public void update(Date lastUpdate) throws CacheModException {
|
||||||
Logger.dev("Repo: Local: " + mLastUpdate + " Remote: " + lastUpdate);
|
|
||||||
if (lastUpdate.after(mLastUpdate)) {
|
if (lastUpdate.after(mLastUpdate)) {
|
||||||
mLastUpdate = lastUpdate;
|
mLastUpdate = lastUpdate;
|
||||||
update();
|
update();
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -18,8 +14,6 @@ public class WebService {
|
|||||||
public final static int GET = 1;
|
public final static int GET = 1;
|
||||||
public final static int POST = 2;
|
public final static int POST = 2;
|
||||||
|
|
||||||
private static Map<String, List<String>> responseHeader;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Making web service call
|
* Making web service call
|
||||||
*
|
*
|
||||||
@ -27,11 +21,11 @@ public class WebService {
|
|||||||
* @requestmethod - http request method
|
* @requestmethod - http request method
|
||||||
*/
|
*/
|
||||||
public static String request(String url, int method) {
|
public static String request(String url, int method) {
|
||||||
return request(url, method, null, null, false);
|
return request(url, method, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String request(String url, int method, boolean newline) {
|
public static String request(String url, int method, boolean newline) {
|
||||||
return request(url, method, null, null, newline);
|
return request(url, method, null, newline);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,8 +38,7 @@ public class WebService {
|
|||||||
* @newline - true to append a newline each line
|
* @newline - true to append a newline each line
|
||||||
*/
|
*/
|
||||||
public static String request(String urlAddress, int method,
|
public static String request(String urlAddress, int method,
|
||||||
Map<String, String> params, Map<String, String> header,
|
Map<String, String> header, boolean newline) {
|
||||||
boolean newline) {
|
|
||||||
Logger.dev("WebService: Service call " + urlAddress);
|
Logger.dev("WebService: Service call " + urlAddress);
|
||||||
URL url;
|
URL url;
|
||||||
StringBuilder response = new StringBuilder();
|
StringBuilder response = new StringBuilder();
|
||||||
@ -69,32 +62,6 @@ public class WebService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params != null) {
|
|
||||||
OutputStream os = conn.getOutputStream();
|
|
||||||
BufferedWriter writer = new BufferedWriter(
|
|
||||||
new OutputStreamWriter(os, "UTF-8"));
|
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
boolean first = true;
|
|
||||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
|
||||||
if (first) {
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
result.append("&");
|
|
||||||
}
|
|
||||||
|
|
||||||
result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
|
|
||||||
result.append("=");
|
|
||||||
result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.write(result.toString());
|
|
||||||
|
|
||||||
writer.flush();
|
|
||||||
writer.close();
|
|
||||||
os.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
int responseCode = conn.getResponseCode();
|
int responseCode = conn.getResponseCode();
|
||||||
|
|
||||||
if (responseCode == HttpsURLConnection.HTTP_OK) {
|
if (responseCode == HttpsURLConnection.HTTP_OK) {
|
||||||
@ -107,9 +74,13 @@ public class WebService {
|
|||||||
response.append(line);
|
response.append(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responseHeader = conn.getHeaderFields();
|
if (header != null) {
|
||||||
} else {
|
header.clear();
|
||||||
responseHeader = null;
|
for (Map.Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
|
||||||
|
List<String> l = entry.getValue();
|
||||||
|
header.put(entry.getKey(), l.get(l.size() - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -118,8 +89,4 @@ public class WebService {
|
|||||||
return response.toString();
|
return response.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, List<String>> getLastResponseHeader() {
|
|
||||||
return responseHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include "zipadjust.h"
|
#include "zipadjust.h"
|
||||||
|
|
||||||
size_t insize = 0, outsize = 0, alloc = 0;
|
size_t insize = 0, outsize = 0, alloc = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user