Remove GSON and switch to database

This commit is contained in:
topjohnwu 2017-02-12 23:26:30 +08:00
parent 1418ec2416
commit 44b0d4127c
13 changed files with 162 additions and 90 deletions

View File

@ -51,9 +51,7 @@ dependencies {
compile 'com.android.support:cardview-v7:25.1.1' compile 'com.android.support:cardview-v7:25.1.1'
compile 'com.android.support:design:25.1.1' compile 'com.android.support:design:25.1.1'
compile 'com.android.support:support-v4:25.1.1' compile 'com.android.support:support-v4:25.1.1'
compile 'com.android.support:support-v13:25.1.1'
compile 'com.jakewharton:butterknife:8.5.1' compile 'com.jakewharton:butterknife:8.5.1'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.github.clans:fab:1.6.4' compile 'com.github.clans:fab:1.6.4'
compile 'com.thoughtbot:expandablerecyclerview:1.4' compile 'com.thoughtbot:expandablerecyclerview:1.4'
compile 'com.madgag.spongycastle:core:1.54.0.0' compile 'com.madgag.spongycastle:core:1.54.0.0'

View File

@ -16,27 +16,6 @@
# public *; # public *;
#} #}
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.topjohnwu.magisk.module.** { *; }
-keep class com.topjohnwu.magisk.module.ModuleHelper$ValueSortedMap { *; }
# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
-keep class android.support.v7.internal.** { *; } -keep class android.support.v7.internal.** { *; }
-keep interface android.support.v7.internal.** { *; } -keep interface android.support.v7.internal.** { *; }
-keep class android.support.v7.** { *; } -keep class android.support.v7.** { *; }

View File

@ -13,7 +13,7 @@ import android.widget.TextView;
import com.topjohnwu.magisk.adapters.SuLogAdapter; import com.topjohnwu.magisk.adapters.SuLogAdapter;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.superuser.SuLogDatabaseHelper; import com.topjohnwu.magisk.database.SuLogDatabaseHelper;
import com.topjohnwu.magisk.superuser.SuLogEntry; import com.topjohnwu.magisk.superuser.SuLogEntry;
import java.util.List; import java.util.List;

View File

@ -11,8 +11,8 @@ import android.widget.TextView;
import com.topjohnwu.magisk.adapters.PolicyAdapter; import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.superuser.Policy; import com.topjohnwu.magisk.superuser.Policy;
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
import java.util.List; import java.util.List;

View File

@ -15,8 +15,8 @@ import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.superuser.Policy; import com.topjohnwu.magisk.superuser.Policy;
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import java.util.HashSet; import java.util.HashSet;

View File

@ -2,10 +2,9 @@ package com.topjohnwu.magisk.asyncs;
import android.app.Activity; import android.app.Activity;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.text.TextUtils;
import com.google.gson.Gson; import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.google.gson.reflect.TypeToken;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.module.BaseModule; import com.topjohnwu.magisk.module.BaseModule;
import com.topjohnwu.magisk.module.Repo; import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
@ -16,6 +15,7 @@ import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.File;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
@ -26,13 +26,14 @@ import java.util.Map;
public class LoadRepos extends ParallelTask<Void, Void, Void> { public class LoadRepos extends ParallelTask<Void, Void, Void> {
public static final String ETAG_KEY = "ETag"; public static final String ETAG_KEY = "ETag";
public static final String VERSION_KEY = "version";
public static final String REPO_KEY = "repomap"; private static final String REPO_URL = "https://api.github.com/orgs/Magisk-Modules-Repo/repos";
public static final String FILE_KEY = "RepoMap";
private static final int GSON_DB_VER = 1; private String prefsPath;
public LoadRepos(Activity context) { public LoadRepos(Activity context) {
super(context); super(context);
prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs";
} }
@Override @Override
@ -41,39 +42,27 @@ public class LoadRepos extends ParallelTask<Void, Void, Void> {
SharedPreferences prefs = magiskManager.prefs; SharedPreferences prefs = magiskManager.prefs;
magiskManager.repoMap = new ValueSortedMap<>(); // Legacy data cleanup
new File(prefsPath, "RepoMap.xml").delete();
Gson gson = new Gson(); prefs.edit().remove("version").remove("repomap").apply();
String jsonString;
int cachedVersion = prefs.getInt(VERSION_KEY, 0);
if (cachedVersion != GSON_DB_VER) {
// Ignore incompatible cached database
jsonString = null;
} else {
jsonString = prefs.getString(REPO_KEY, null);
}
Map<String, Repo> cached = null;
if (jsonString != null) {
cached = gson.fromJson(jsonString, new TypeToken<ValueSortedMap<String, Repo>>(){}.getType());
}
if (cached == null) {
cached = new ValueSortedMap<>();
}
Map<String, String> header = new HashMap<>();
// Get cached ETag to add in the request header // Get cached ETag to add in the request header
String etag = prefs.getString(ETAG_KEY, ""); String etag = prefs.getString(ETAG_KEY, "");
Map<String, String> header = new HashMap<>();
// Add header only if db exists
if (magiskManager.getDatabasePath("repo.db").exists())
header.put("If-None-Match", etag); header.put("If-None-Match", etag);
// Making a request to main URL for repo info magiskManager.repoMap = new ValueSortedMap<>();
jsonString = WebService.request(
magiskManager.getString(R.string.url_main), WebService.GET, null, header, false);
if (!jsonString.isEmpty()) { // Make a request to main URL for repo info
String jsonString = WebService.request(REPO_URL, WebService.GET, null, header, false);
RepoDatabaseHelper dbHelper = new RepoDatabaseHelper(magiskManager);
ValueSortedMap<String, Repo> cached = dbHelper.getRepoMap();
if (!TextUtils.isEmpty(jsonString)) {
try { try {
JSONArray jsonArray = new JSONArray(jsonString); JSONArray jsonArray = new JSONArray(jsonString);
// If it gets to this point, the response is valid, update ETag // If it gets to this point, the response is valid, update ETag
@ -98,16 +87,18 @@ public class LoadRepos extends ParallelTask<Void, Void, Void> {
try { try {
if (repo == null) { if (repo == null) {
Logger.dev("LoadRepos: Create new repo " + id); Logger.dev("LoadRepos: Create new repo " + id);
repo = new Repo(magiskManager, name, updatedDate); repo = new Repo(name, updatedDate);
} else { } else {
// Popout from cached
cached.remove(id);
Logger.dev("LoadRepos: Update cached repo " + id); Logger.dev("LoadRepos: Update cached repo " + id);
repo.update(updatedDate); repo.update(updatedDate);
} }
if (repo.getId() != null) { if (repo.getId() != null)
magiskManager.repoMap.put(id, repo); magiskManager.repoMap.put(id, repo);
}
} catch (BaseModule.CacheModException ignored) {} } catch (BaseModule.CacheModException ignored) {}
} }
} catch (JSONException e) { } catch (JSONException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -115,13 +106,15 @@ public class LoadRepos extends ParallelTask<Void, Void, Void> {
// Use cached if no internet or no updates // Use cached if no internet or no updates
Logger.dev("LoadRepos: No updates, use cached"); Logger.dev("LoadRepos: No updates, use cached");
magiskManager.repoMap.putAll(cached); magiskManager.repoMap.putAll(cached);
cached.clear();
} }
prefs.edit() // Update the database
.putInt(VERSION_KEY, GSON_DB_VER) dbHelper.addRepoMap(magiskManager.repoMap);
.putString(REPO_KEY, gson.toJson(magiskManager.repoMap)) // The leftover cached are those removed remote, cleanup db
.putString(ETAG_KEY, etag) dbHelper.removeRepo(cached);
.apply(); // Update ETag
prefs.edit().putString(ETAG_KEY, etag).apply();
Logger.dev("LoadRepos: Repo load done"); Logger.dev("LoadRepos: Repo load done");
return null; return null;

View File

@ -0,0 +1,74 @@
package com.topjohnwu.magisk.database;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.utils.ValueSortedMap;
import java.util.Collection;
public class RepoDatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VER = 1;
private static final String TABLE_NAME = "repos";
public RepoDatabaseHelper(Context context) {
super(context, "repo.db", null, DATABASE_VER);
}
@Override
public void onCreate(SQLiteDatabase db) {
onUpgrade(db, 0, DATABASE_VER);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 0) {
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(id TEXT, name TEXT, version TEXT, versionCode INT, " +
"author TEXT, description TEXT, repo_name TEXT, last_update INT, " +
"PRIMARY KEY(id))");
}
// No upgrades yet
}
public void addRepoMap(ValueSortedMap<String, Repo> map) {
SQLiteDatabase db = getWritableDatabase();
Collection<Repo> list = map.values();
for (Repo repo : list)
db.replace(TABLE_NAME, null, repo.getContentValues());
db.close();
}
public void clearRepo() {
SQLiteDatabase db = getWritableDatabase();
db.delete(TABLE_NAME, null, null);
db.close();
}
public void removeRepo(ValueSortedMap<String, Repo> map) {
SQLiteDatabase db = getWritableDatabase();
Collection<Repo> list = map.values();
for (Repo repo : list)
db.delete(TABLE_NAME, "id=?", new String[] { repo.getId() });
db.close();
}
public ValueSortedMap<String, Repo> getRepoMap() {
ValueSortedMap<String, Repo> ret = new ValueSortedMap<>();
SQLiteDatabase db = getReadableDatabase();
Repo repo;
try (Cursor c = db.query(TABLE_NAME, null, null, null, null, null, null)) {
while (c.moveToNext()) {
repo = new Repo(c);
ret.put(repo.getId(), repo);
}
}
db.close();
return ret;
}
}

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.superuser; package com.topjohnwu.magisk.database;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -6,6 +6,8 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import com.topjohnwu.magisk.superuser.Policy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.superuser; package com.topjohnwu.magisk.database;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.superuser.SuLogEntry;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -1,34 +1,47 @@
package com.topjohnwu.magisk.module; package com.topjohnwu.magisk.module;
import android.content.Context; import android.content.ContentValues;
import android.database.Cursor;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.WebService; import com.topjohnwu.magisk.utils.WebService;
import java.util.Date; import java.util.Date;
public class Repo extends BaseModule { public class Repo extends BaseModule {
private String mLogUrl, mManifestUrl, mZipUrl;
private static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s";
private static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip";
private String repoName;
private Date mLastUpdate; private Date mLastUpdate;
public Repo(Context context, String name, Date lastUpdate) throws CacheModException { public Repo(String name, Date lastUpdate) throws CacheModException {
mLastUpdate = lastUpdate; mLastUpdate = lastUpdate;
mLogUrl = context.getString(R.string.file_url, name, "changelog.txt"); repoName = name;
mManifestUrl = context.getString(R.string.file_url, name, "module.prop");
mZipUrl = context.getString(R.string.zip_url, name);
update(); update();
} }
public Repo(Cursor c) {
mId = c.getString(c.getColumnIndex("id"));
mName = c.getString(c.getColumnIndex("name"));
mVersion = c.getString(c.getColumnIndex("version"));
mVersionCode = c.getInt(c.getColumnIndex("versionCode"));
mAuthor = c.getString(c.getColumnIndex("author"));
mDescription = c.getString(c.getColumnIndex("description"));
repoName = c.getString(c.getColumnIndex("repo_name"));
mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update")));
}
public void update() throws CacheModException { public void update() throws CacheModException {
Logger.dev("Repo: Re-fetch prop"); Logger.dev("Repo: Re-fetch prop");
String props = WebService.request(mManifestUrl, WebService.GET, true); String props = WebService.request(getManifestUrl(), WebService.GET, true);
String lines[] = props.split("\\n"); String lines[] = props.split("\\n");
parseProps(lines); parseProps(lines);
} }
public void update(Date lastUpdate) throws CacheModException { public void update(Date lastUpdate) throws CacheModException {
Logger.dev("Repo: Old: " + mLastUpdate + " New: " + lastUpdate); Logger.dev("Repo: Local: " + mLastUpdate + " Remote: " + lastUpdate);
if (mIsCacheModule) if (mIsCacheModule)
throw new CacheModException(mId); throw new CacheModException(mId);
if (lastUpdate.after(mLastUpdate)) { if (lastUpdate.after(mLastUpdate)) {
@ -37,16 +50,29 @@ public class Repo extends BaseModule {
} }
} }
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("id", mId);
values.put("name", mName);
values.put("version", mVersion);
values.put("versionCode", mVersionCode);
values.put("author", mAuthor);
values.put("description", mDescription);
values.put("repo_name", repoName);
values.put("last_update", mLastUpdate.getTime());
return values;
}
public String getZipUrl() { public String getZipUrl() {
return mZipUrl; return String.format(ZIP_URL, repoName);
} }
public String getLogUrl() { public String getLogUrl() {
return mLogUrl; return String.format(FILE_URL, repoName, "changelog.txt");
} }
public String getManifestUrl() { public String getManifestUrl() {
return mManifestUrl; return String.format(FILE_URL, repoName, "module.prop");
} }
public Date getLastUpdate() { public Date getLastUpdate() {

View File

@ -8,6 +8,8 @@ import android.widget.Toast;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.database.SuLogDatabaseHelper;
import java.util.Date; import java.util.Date;

View File

@ -21,6 +21,7 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.ParallelTask; import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.components.Activity; import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.utils.CallbackEvent; import com.topjohnwu.magisk.utils.CallbackEvent;
import java.io.DataInputStream; import java.io.DataInputStream;

View File

@ -17,6 +17,7 @@ import android.widget.Toast;
import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.LoadRepos; import com.topjohnwu.magisk.asyncs.LoadRepos;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.receivers.DownloadReceiver; import com.topjohnwu.magisk.receivers.DownloadReceiver;
import java.io.File; import java.io.File;
@ -157,13 +158,8 @@ public class Utils {
public static void clearRepoCache(Activity activity) { public static void clearRepoCache(Activity activity) {
MagiskManager magiskManager = getMagiskManager(activity); MagiskManager magiskManager = getMagiskManager(activity);
SharedPreferences repoMap = activity.getSharedPreferences(LoadRepos.FILE_KEY, Context.MODE_PRIVATE); magiskManager.prefs.edit().remove(LoadRepos.ETAG_KEY).apply();
repoMap.edit() new RepoDatabaseHelper(activity).clearRepo();
.remove(LoadRepos.ETAG_KEY)
.remove(LoadRepos.VERSION_KEY)
.apply();
magiskManager.repoLoadDone.isTriggered = false;
new LoadRepos(activity).exec();
Toast.makeText(activity, R.string.repo_cache_cleared, Toast.LENGTH_SHORT).show(); Toast.makeText(activity, R.string.repo_cache_cleared, Toast.LENGTH_SHORT).show();
} }
} }