From 44b0d4127cbf3bd98876ea0eed745c6bcd92559d Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 12 Feb 2017 23:26:30 +0800 Subject: [PATCH] Remove GSON and switch to database --- app/build.gradle | 2 - app/proguard-rules.pro | 21 ----- .../com/topjohnwu/magisk/SuLogFragment.java | 2 +- .../topjohnwu/magisk/SuperuserFragment.java | 2 +- .../magisk/adapters/PolicyAdapter.java | 2 +- .../topjohnwu/magisk/asyncs/LoadRepos.java | 79 +++++++++---------- .../magisk/database/RepoDatabaseHelper.java | 74 +++++++++++++++++ .../SuDatabaseHelper.java | 4 +- .../SuLogDatabaseHelper.java | 3 +- .../com/topjohnwu/magisk/module/Repo.java | 50 +++++++++--- .../magisk/superuser/SuReceiver.java | 2 + .../magisk/superuser/SuRequestActivity.java | 1 + .../com/topjohnwu/magisk/utils/Utils.java | 10 +-- 13 files changed, 162 insertions(+), 90 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/database/RepoDatabaseHelper.java rename app/src/main/java/com/topjohnwu/magisk/{superuser => database}/SuDatabaseHelper.java (96%) rename app/src/main/java/com/topjohnwu/magisk/{superuser => database}/SuLogDatabaseHelper.java (96%) diff --git a/app/build.gradle b/app/build.gradle index 6d6a9739d..bfc9efb0b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,9 +51,7 @@ dependencies { compile 'com.android.support:cardview-v7: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-v13:25.1.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.thoughtbot:expandablerecyclerview:1.4' compile 'com.madgag.spongycastle:core:1.54.0.0' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index e317e56cd..af0e0a2f0 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -16,27 +16,6 @@ # 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 interface android.support.v7.internal.** { *; } -keep class android.support.v7.** { *; } diff --git a/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java b/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java index b6b40b23a..bacaeae18 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java @@ -13,7 +13,7 @@ import android.widget.TextView; import com.topjohnwu.magisk.adapters.SuLogAdapter; 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 java.util.List; diff --git a/app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java b/app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java index 7ca2b97d7..08adb511f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java @@ -11,8 +11,8 @@ import android.widget.TextView; import com.topjohnwu.magisk.adapters.PolicyAdapter; import com.topjohnwu.magisk.components.Fragment; +import com.topjohnwu.magisk.database.SuDatabaseHelper; import com.topjohnwu.magisk.superuser.Policy; -import com.topjohnwu.magisk.superuser.SuDatabaseHelper; import java.util.List; diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java b/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java index dcf628ac2..b96987e20 100644 --- a/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java +++ b/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java @@ -15,8 +15,8 @@ import android.widget.Switch; import android.widget.TextView; import com.topjohnwu.magisk.R; +import com.topjohnwu.magisk.database.SuDatabaseHelper; import com.topjohnwu.magisk.superuser.Policy; -import com.topjohnwu.magisk.superuser.SuDatabaseHelper; import com.topjohnwu.magisk.utils.Utils; import java.util.HashSet; diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadRepos.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadRepos.java index 9bf47c6f3..71994e85f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadRepos.java +++ b/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadRepos.java @@ -2,10 +2,9 @@ package com.topjohnwu.magisk.asyncs; import android.app.Activity; import android.content.SharedPreferences; +import android.text.TextUtils; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import com.topjohnwu.magisk.R; +import com.topjohnwu.magisk.database.RepoDatabaseHelper; import com.topjohnwu.magisk.module.BaseModule; import com.topjohnwu.magisk.module.Repo; import com.topjohnwu.magisk.utils.Logger; @@ -16,6 +15,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.io.File; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @@ -26,13 +26,14 @@ import java.util.Map; public class LoadRepos extends ParallelTask { public static final String ETAG_KEY = "ETag"; - public static final String VERSION_KEY = "version"; - public static final String REPO_KEY = "repomap"; - public static final String FILE_KEY = "RepoMap"; - private static final int GSON_DB_VER = 1; + + private static final String REPO_URL = "https://api.github.com/orgs/Magisk-Modules-Repo/repos"; + + private String prefsPath; public LoadRepos(Activity context) { super(context); + prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs"; } @Override @@ -41,39 +42,27 @@ public class LoadRepos extends ParallelTask { SharedPreferences prefs = magiskManager.prefs; - magiskManager.repoMap = new ValueSortedMap<>(); - - Gson gson = new Gson(); - 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 cached = null; - - if (jsonString != null) { - cached = gson.fromJson(jsonString, new TypeToken>(){}.getType()); - } - - if (cached == null) { - cached = new ValueSortedMap<>(); - } + // Legacy data cleanup + new File(prefsPath, "RepoMap.xml").delete(); + prefs.edit().remove("version").remove("repomap").apply(); + Map header = new HashMap<>(); // Get cached ETag to add in the request header String etag = prefs.getString(ETAG_KEY, ""); - Map header = new HashMap<>(); - header.put("If-None-Match", etag); - // Making a request to main URL for repo info - jsonString = WebService.request( - magiskManager.getString(R.string.url_main), WebService.GET, null, header, false); + // Add header only if db exists + if (magiskManager.getDatabasePath("repo.db").exists()) + header.put("If-None-Match", etag); - if (!jsonString.isEmpty()) { + magiskManager.repoMap = new ValueSortedMap<>(); + + // 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 cached = dbHelper.getRepoMap(); + + if (!TextUtils.isEmpty(jsonString)) { try { JSONArray jsonArray = new JSONArray(jsonString); // If it gets to this point, the response is valid, update ETag @@ -98,16 +87,18 @@ public class LoadRepos extends ParallelTask { try { if (repo == null) { Logger.dev("LoadRepos: Create new repo " + id); - repo = new Repo(magiskManager, name, updatedDate); + 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) { + if (repo.getId() != null) magiskManager.repoMap.put(id, repo); - } } catch (BaseModule.CacheModException ignored) {} } + } catch (JSONException e) { e.printStackTrace(); } @@ -115,13 +106,15 @@ public class LoadRepos extends ParallelTask { // Use cached if no internet or no updates Logger.dev("LoadRepos: No updates, use cached"); magiskManager.repoMap.putAll(cached); + cached.clear(); } - prefs.edit() - .putInt(VERSION_KEY, GSON_DB_VER) - .putString(REPO_KEY, gson.toJson(magiskManager.repoMap)) - .putString(ETAG_KEY, etag) - .apply(); + // Update the database + dbHelper.addRepoMap(magiskManager.repoMap); + // The leftover cached are those removed remote, cleanup db + dbHelper.removeRepo(cached); + // Update ETag + prefs.edit().putString(ETAG_KEY, etag).apply(); Logger.dev("LoadRepos: Repo load done"); return null; diff --git a/app/src/main/java/com/topjohnwu/magisk/database/RepoDatabaseHelper.java b/app/src/main/java/com/topjohnwu/magisk/database/RepoDatabaseHelper.java new file mode 100644 index 000000000..37a150412 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/database/RepoDatabaseHelper.java @@ -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 map) { + SQLiteDatabase db = getWritableDatabase(); + Collection 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 map) { + SQLiteDatabase db = getWritableDatabase(); + Collection list = map.values(); + for (Repo repo : list) + db.delete(TABLE_NAME, "id=?", new String[] { repo.getId() }); + db.close(); + } + + public ValueSortedMap getRepoMap() { + ValueSortedMap 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; + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/superuser/SuDatabaseHelper.java b/app/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java similarity index 96% rename from app/src/main/java/com/topjohnwu/magisk/superuser/SuDatabaseHelper.java rename to app/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java index 21cf457c0..c5ac90eab 100644 --- a/app/src/main/java/com/topjohnwu/magisk/superuser/SuDatabaseHelper.java +++ b/app/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.superuser; +package com.topjohnwu.magisk.database; import android.content.Context; import android.content.pm.PackageManager; @@ -6,6 +6,8 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import com.topjohnwu.magisk.superuser.Policy; + import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/topjohnwu/magisk/superuser/SuLogDatabaseHelper.java b/app/src/main/java/com/topjohnwu/magisk/database/SuLogDatabaseHelper.java similarity index 96% rename from app/src/main/java/com/topjohnwu/magisk/superuser/SuLogDatabaseHelper.java rename to app/src/main/java/com/topjohnwu/magisk/database/SuLogDatabaseHelper.java index 0201280aa..05f3eee29 100644 --- a/app/src/main/java/com/topjohnwu/magisk/superuser/SuLogDatabaseHelper.java +++ b/app/src/main/java/com/topjohnwu/magisk/database/SuLogDatabaseHelper.java @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.superuser; +package com.topjohnwu.magisk.database; import android.content.Context; import android.database.Cursor; @@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import com.topjohnwu.magisk.MagiskManager; +import com.topjohnwu.magisk.superuser.SuLogEntry; import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/topjohnwu/magisk/module/Repo.java b/app/src/main/java/com/topjohnwu/magisk/module/Repo.java index 41da8c2fa..17310e5b5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/module/Repo.java +++ b/app/src/main/java/com/topjohnwu/magisk/module/Repo.java @@ -1,34 +1,47 @@ 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.WebService; import java.util.Date; 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; - public Repo(Context context, String name, Date lastUpdate) throws CacheModException { + public Repo(String name, Date lastUpdate) throws CacheModException { mLastUpdate = lastUpdate; - mLogUrl = context.getString(R.string.file_url, name, "changelog.txt"); - mManifestUrl = context.getString(R.string.file_url, name, "module.prop"); - mZipUrl = context.getString(R.string.zip_url, name); + repoName = name; 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 { 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"); parseProps(lines); } public void update(Date lastUpdate) throws CacheModException { - Logger.dev("Repo: Old: " + mLastUpdate + " New: " + lastUpdate); + Logger.dev("Repo: Local: " + mLastUpdate + " Remote: " + lastUpdate); if (mIsCacheModule) throw new CacheModException(mId); 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() { - return mZipUrl; + return String.format(ZIP_URL, repoName); } public String getLogUrl() { - return mLogUrl; + return String.format(FILE_URL, repoName, "changelog.txt"); } public String getManifestUrl() { - return mManifestUrl; + return String.format(FILE_URL, repoName, "module.prop"); } public Date getLastUpdate() { diff --git a/app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java b/app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java index 5a43d023f..f4bde58b0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java +++ b/app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java @@ -8,6 +8,8 @@ import android.widget.Toast; import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.R; +import com.topjohnwu.magisk.database.SuDatabaseHelper; +import com.topjohnwu.magisk.database.SuLogDatabaseHelper; import java.util.Date; diff --git a/app/src/main/java/com/topjohnwu/magisk/superuser/SuRequestActivity.java b/app/src/main/java/com/topjohnwu/magisk/superuser/SuRequestActivity.java index 1c3fa2d7a..f419de8ad 100644 --- a/app/src/main/java/com/topjohnwu/magisk/superuser/SuRequestActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/superuser/SuRequestActivity.java @@ -21,6 +21,7 @@ import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.asyncs.ParallelTask; import com.topjohnwu.magisk.components.Activity; +import com.topjohnwu.magisk.database.SuDatabaseHelper; import com.topjohnwu.magisk.utils.CallbackEvent; import java.io.DataInputStream; diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java index 7d6abf978..ab43aef2c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java @@ -17,6 +17,7 @@ import android.widget.Toast; import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.asyncs.LoadRepos; +import com.topjohnwu.magisk.database.RepoDatabaseHelper; import com.topjohnwu.magisk.receivers.DownloadReceiver; import java.io.File; @@ -157,13 +158,8 @@ public class Utils { public static void clearRepoCache(Activity activity) { MagiskManager magiskManager = getMagiskManager(activity); - SharedPreferences repoMap = activity.getSharedPreferences(LoadRepos.FILE_KEY, Context.MODE_PRIVATE); - repoMap.edit() - .remove(LoadRepos.ETAG_KEY) - .remove(LoadRepos.VERSION_KEY) - .apply(); - magiskManager.repoLoadDone.isTriggered = false; - new LoadRepos(activity).exec(); + magiskManager.prefs.edit().remove(LoadRepos.ETAG_KEY).apply(); + new RepoDatabaseHelper(activity).clearRepo(); Toast.makeText(activity, R.string.repo_cache_cleared, Toast.LENGTH_SHORT).show(); } } \ No newline at end of file