2017-02-12 12:49:46 +01:00
|
|
|
package com.topjohnwu.magisk.asyncs;
|
|
|
|
|
2018-07-04 14:13:12 +02:00
|
|
|
import android.database.Cursor;
|
2017-10-13 20:51:42 +02:00
|
|
|
import android.os.AsyncTask;
|
2018-07-04 14:13:12 +02:00
|
|
|
import android.text.TextUtils;
|
2017-02-12 12:49:46 +01:00
|
|
|
|
2017-07-19 12:01:22 +02:00
|
|
|
import com.topjohnwu.magisk.MagiskManager;
|
2017-09-29 19:15:34 +02:00
|
|
|
import com.topjohnwu.magisk.ReposFragment;
|
2017-09-29 21:04:23 +02:00
|
|
|
import com.topjohnwu.magisk.container.Repo;
|
2017-11-05 21:41:23 +01:00
|
|
|
import com.topjohnwu.magisk.utils.Const;
|
2017-10-13 20:51:42 +02:00
|
|
|
import com.topjohnwu.magisk.utils.Logger;
|
2018-05-05 20:51:23 +02:00
|
|
|
import com.topjohnwu.magisk.utils.Utils;
|
2017-02-12 13:53:41 +01:00
|
|
|
import com.topjohnwu.magisk.utils.WebService;
|
|
|
|
|
|
|
|
import org.json.JSONArray;
|
2018-07-04 14:13:12 +02:00
|
|
|
import org.json.JSONException;
|
2017-02-12 13:53:41 +01:00
|
|
|
import org.json.JSONObject;
|
|
|
|
|
2017-09-30 19:28:50 +02:00
|
|
|
import java.net.HttpURLConnection;
|
2018-04-06 22:47:22 +02:00
|
|
|
import java.text.DateFormat;
|
2018-07-04 14:13:12 +02:00
|
|
|
import java.text.ParseException;
|
2017-02-12 13:53:41 +01:00
|
|
|
import java.text.SimpleDateFormat;
|
2017-04-25 18:14:01 +02:00
|
|
|
import java.util.Arrays;
|
2018-07-04 14:13:12 +02:00
|
|
|
import java.util.Collections;
|
2017-02-12 13:53:41 +01:00
|
|
|
import java.util.Date;
|
|
|
|
import java.util.HashMap;
|
2018-05-05 20:51:23 +02:00
|
|
|
import java.util.LinkedList;
|
2017-04-25 18:14:01 +02:00
|
|
|
import java.util.List;
|
2017-02-12 13:53:41 +01:00
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Map;
|
2018-07-04 14:13:12 +02:00
|
|
|
import java.util.Set;
|
2018-04-06 22:47:22 +02:00
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
2017-02-12 12:49:46 +01:00
|
|
|
|
2017-07-20 20:46:02 +02:00
|
|
|
public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
2017-02-12 12:49:46 +01:00
|
|
|
|
2017-04-25 18:14:01 +02:00
|
|
|
private static final int CHECK_ETAG = 0;
|
|
|
|
private static final int LOAD_NEXT = 1;
|
|
|
|
private static final int LOAD_PREV = 2;
|
2018-04-06 22:47:22 +02:00
|
|
|
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
2017-04-25 18:14:01 +02:00
|
|
|
|
2018-04-06 22:47:22 +02:00
|
|
|
private MagiskManager mm;
|
2018-07-04 14:13:12 +02:00
|
|
|
private List<String> etags, newEtags = new LinkedList<>();
|
|
|
|
private Set<String> cached;
|
2017-10-13 20:51:42 +02:00
|
|
|
private boolean forceUpdate;
|
2018-04-06 22:47:22 +02:00
|
|
|
private AtomicInteger taskCount = new AtomicInteger(0);
|
|
|
|
final private Object allDone = new Object();
|
2017-10-13 20:51:42 +02:00
|
|
|
|
2017-10-15 18:54:48 +02:00
|
|
|
public UpdateRepos(boolean force) {
|
2018-04-06 22:47:22 +02:00
|
|
|
mm = MagiskManager.get();
|
2018-03-17 13:20:05 +01:00
|
|
|
mm.repoLoadDone.reset();
|
2017-10-13 20:51:42 +02:00
|
|
|
forceUpdate = force;
|
2017-04-25 18:14:01 +02:00
|
|
|
}
|
2017-02-12 13:53:41 +01:00
|
|
|
|
2018-04-06 22:47:22 +02:00
|
|
|
private void queueTask(Runnable task) {
|
|
|
|
// Thread pool's queue has an upper bound, batch it with 64 tasks
|
|
|
|
while (taskCount.get() >= 64) {
|
|
|
|
waitTasks();
|
|
|
|
}
|
|
|
|
taskCount.incrementAndGet();
|
|
|
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
|
|
|
task.run();
|
|
|
|
if (taskCount.decrementAndGet() == 0) {
|
|
|
|
synchronized (allDone) {
|
|
|
|
allDone.notify();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void waitTasks() {
|
|
|
|
if (taskCount.get() == 0)
|
|
|
|
return;
|
|
|
|
synchronized (allDone) {
|
|
|
|
try {
|
|
|
|
allDone.wait();
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
// Wait again
|
|
|
|
waitTasks();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-04 14:13:12 +02:00
|
|
|
private boolean loadJSON(String jsonString) throws JSONException, ParseException {
|
2017-04-25 18:14:01 +02:00
|
|
|
JSONArray jsonArray = new JSONArray(jsonString);
|
|
|
|
|
2018-07-04 14:13:12 +02:00
|
|
|
// Empty page, halt
|
2018-04-06 22:47:22 +02:00
|
|
|
if (jsonArray.length() == 0)
|
2018-07-04 14:13:12 +02:00
|
|
|
return false;
|
2017-09-30 19:28:50 +02:00
|
|
|
|
2017-04-25 18:14:01 +02:00
|
|
|
for (int i = 0; i < jsonArray.length(); i++) {
|
2018-04-06 22:47:22 +02:00
|
|
|
JSONObject rawRepo = jsonArray.getJSONObject(i);
|
|
|
|
String id = rawRepo.getString("description");
|
|
|
|
String name = rawRepo.getString("name");
|
|
|
|
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
|
2018-07-04 14:13:12 +02:00
|
|
|
Set<String> set = Collections.synchronizedSet(cached);
|
2018-04-06 22:47:22 +02:00
|
|
|
queueTask(() -> {
|
|
|
|
Repo repo = mm.repoDB.getRepo(id);
|
2017-10-13 20:51:42 +02:00
|
|
|
try {
|
2018-07-04 14:13:12 +02:00
|
|
|
if (repo == null)
|
|
|
|
repo = new Repo(name);
|
|
|
|
else
|
|
|
|
set.remove(id);
|
|
|
|
repo.update(date);
|
|
|
|
mm.repoDB.addRepo(repo);
|
|
|
|
publishProgress();
|
2017-10-13 20:51:42 +02:00
|
|
|
} catch (Repo.IllegalRepoException e) {
|
2018-07-04 12:11:57 +02:00
|
|
|
Logger.debug(e.getMessage());
|
2018-04-06 22:47:22 +02:00
|
|
|
mm.repoDB.removeRepo(id);
|
2017-10-13 20:51:42 +02:00
|
|
|
}
|
|
|
|
});
|
2017-04-25 18:14:01 +02:00
|
|
|
}
|
2018-07-04 14:13:12 +02:00
|
|
|
return true;
|
2017-04-25 18:14:01 +02:00
|
|
|
}
|
|
|
|
|
2017-09-30 19:28:50 +02:00
|
|
|
private boolean loadPage(int page, int mode) {
|
2017-02-12 16:26:30 +01:00
|
|
|
Map<String, String> header = new HashMap<>();
|
2018-07-04 14:13:12 +02:00
|
|
|
if (mode == CHECK_ETAG && page < etags.size())
|
|
|
|
header.put(Const.Key.IF_NONE_MATCH, etags.get(page));
|
2018-05-05 20:51:23 +02:00
|
|
|
String url = Utils.fmt(Const.Url.REPO_URL, page + 1);
|
2017-02-12 13:53:41 +01:00
|
|
|
|
2017-04-25 18:14:01 +02:00
|
|
|
try {
|
2018-07-04 14:13:12 +02:00
|
|
|
HttpURLConnection conn = WebService.request(url, header);
|
2017-09-30 19:28:50 +02:00
|
|
|
if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
2018-05-05 20:51:23 +02:00
|
|
|
// Current page is not updated, check the next page
|
2018-07-04 14:13:12 +02:00
|
|
|
return loadPage(page + 1, CHECK_ETAG);
|
2017-09-30 19:28:50 +02:00
|
|
|
}
|
2018-07-04 14:13:12 +02:00
|
|
|
if (!loadJSON(WebService.getString(conn)))
|
|
|
|
return mode != CHECK_ETAG;
|
2017-04-25 18:14:01 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2018-07-04 14:13:12 +02:00
|
|
|
return false;
|
2017-04-25 18:14:01 +02:00
|
|
|
}
|
2017-02-12 13:53:41 +01:00
|
|
|
|
2018-05-05 20:51:23 +02:00
|
|
|
/* If one page is updated, we force update all pages */
|
|
|
|
|
2017-09-30 19:28:50 +02:00
|
|
|
// Update ETAG
|
2018-07-04 14:13:12 +02:00
|
|
|
String etag = header.get(Const.Key.ETAG_KEY);
|
2017-09-30 19:28:50 +02:00
|
|
|
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
2018-05-05 20:51:23 +02:00
|
|
|
if (mode == LOAD_PREV) {
|
|
|
|
// We are loading a previous page, push the new tag to the front
|
|
|
|
newEtags.add(0, etag);
|
|
|
|
} else {
|
|
|
|
newEtags.add(etag);
|
|
|
|
}
|
2017-02-12 13:53:41 +01:00
|
|
|
|
2017-11-05 21:41:23 +01:00
|
|
|
String links = header.get(Const.Key.LINK_KEY);
|
2017-04-25 18:14:01 +02:00
|
|
|
if (links != null) {
|
2017-09-30 19:28:50 +02:00
|
|
|
for (String s : links.split(", ")) {
|
|
|
|
if (mode != LOAD_PREV && s.contains("next")) {
|
|
|
|
// Force load all next pages
|
|
|
|
loadPage(page + 1, LOAD_NEXT);
|
2018-05-05 20:51:23 +02:00
|
|
|
}
|
|
|
|
if (mode != LOAD_NEXT && s.contains("prev")) {
|
2017-09-30 19:28:50 +02:00
|
|
|
// Back propagation
|
|
|
|
loadPage(page - 1, LOAD_PREV);
|
2017-04-25 18:14:01 +02:00
|
|
|
}
|
2017-02-12 13:53:41 +01:00
|
|
|
}
|
|
|
|
}
|
2017-04-25 18:14:01 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-29 19:15:34 +02:00
|
|
|
@Override
|
|
|
|
protected void onProgressUpdate(Void... values) {
|
|
|
|
if (ReposFragment.adapter != null)
|
|
|
|
ReposFragment.adapter.notifyDBChanged();
|
|
|
|
}
|
|
|
|
|
2018-03-17 13:20:05 +01:00
|
|
|
@Override
|
|
|
|
protected void onPreExecute() {
|
2018-04-06 22:47:22 +02:00
|
|
|
mm.repoLoadDone.setPending();
|
2018-03-17 13:20:05 +01:00
|
|
|
}
|
|
|
|
|
2017-04-25 18:14:01 +02:00
|
|
|
@Override
|
|
|
|
protected Void doInBackground(Void... voids) {
|
2018-07-04 14:13:12 +02:00
|
|
|
etags = Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(","));
|
|
|
|
cached = mm.repoDB.getRepoIDSet();
|
|
|
|
|
|
|
|
if (loadPage(0, CHECK_ETAG)) {
|
2018-04-06 22:47:22 +02:00
|
|
|
waitTasks();
|
|
|
|
|
|
|
|
// The leftover cached means they are removed from online repo
|
|
|
|
mm.repoDB.removeRepo(cached);
|
|
|
|
|
|
|
|
// Update ETag
|
2018-07-04 14:13:12 +02:00
|
|
|
mm.prefs.edit().putString(Const.Key.ETAG_KEY, TextUtils.join(",", newEtags)).apply();
|
|
|
|
} else if (forceUpdate) {
|
|
|
|
Cursor c = mm.repoDB.getRawCursor();
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
Repo repo = new Repo(c);
|
|
|
|
queueTask(() -> {
|
|
|
|
try {
|
|
|
|
repo.update();
|
|
|
|
mm.repoDB.addRepo(repo);
|
|
|
|
} catch (Repo.IllegalRepoException e) {
|
|
|
|
Logger.debug(e.getMessage());
|
|
|
|
mm.repoDB.removeRepo(repo);
|
|
|
|
}
|
|
|
|
});
|
2018-04-06 22:47:22 +02:00
|
|
|
}
|
2018-07-04 14:13:12 +02:00
|
|
|
waitTasks();
|
2017-04-25 18:14:01 +02:00
|
|
|
}
|
2017-02-12 12:49:46 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Void v) {
|
2018-04-06 22:47:22 +02:00
|
|
|
mm.repoLoadDone.publish();
|
2017-06-05 21:06:23 +02:00
|
|
|
super.onPostExecute(v);
|
2017-02-12 12:49:46 +01:00
|
|
|
}
|
|
|
|
}
|