diff --git a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java index 3c47c485f..a29899c89 100644 --- a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java +++ b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java @@ -58,6 +58,7 @@ import java.security.CodeSigner; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.jar.JarEntry; @@ -195,9 +196,11 @@ public class RepoUpdater { private RepoXMLHandler.IndexReceiver createIndexReceiver() { return new RepoXMLHandler.IndexReceiver() { @Override - public void receiveRepo(String name, String description, String signingCert, int maxAge, int version, long timestamp) { + public void receiveRepo(String name, String description, String signingCert, int maxAge, + int version, long timestamp, String icon, String[] mirrors) { signingCertFromIndexXml = signingCert; - repoDetailsToSave = prepareRepoDetailsForSaving(name, description, maxAge, version, timestamp); + repoDetailsToSave = prepareRepoDetailsForSaving(name, description, maxAge, version, + timestamp, icon, mirrors); } @Override @@ -294,7 +297,9 @@ public class RepoUpdater { * Update tracking data for the repo represented by this instance (index version, etag, * description, human-readable name, etc. */ - private ContentValues prepareRepoDetailsForSaving(String name, String description, int maxAge, int version, long timestamp) { + private ContentValues prepareRepoDetailsForSaving(String name, String description, int maxAge, + int version, long timestamp, String icon, + String[] mirrors) { ContentValues values = new ContentValues(); values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatTime(new Date(), "")); @@ -329,6 +334,14 @@ public class RepoUpdater { // timestamp. values.put(RepoTable.Cols.TIMESTAMP, timestamp); + if (icon != null && !icon.equals(repo.icon)) { + values.put(RepoTable.Cols.ICON, icon); + } + + if (mirrors != null && mirrors.length > 0 && !Arrays.equals(mirrors, repo.mirrors)) { + values.put(RepoTable.Cols.MIRRORS, Utils.serializeCommaSeparatedString(mirrors)); + } + return values; } diff --git a/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java b/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java index e822348c7..345b0d890 100644 --- a/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java +++ b/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java @@ -59,6 +59,8 @@ public class RepoXMLHandler extends DefaultHandler { private long repoTimestamp; private String repoDescription; private String repoName; + private String repoIcon; + private final ArrayList repoMirrors = new ArrayList<>(); /** * Set of requested permissions per package/APK @@ -73,7 +75,8 @@ public class RepoXMLHandler extends DefaultHandler { private final StringBuilder curchars = new StringBuilder(); public interface IndexReceiver { - void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp); + void receiveRepo(String name, String description, String signingCert, int maxage, int version, + long timestamp, String icon, String[] mirrors); void receiveApp(App app, List packages); @@ -258,6 +261,8 @@ public class RepoXMLHandler extends DefaultHandler { } } else if ("description".equals(localName)) { repoDescription = cleanWhiteSpace(str); + } else if ("mirror".equals(localName)) { + repoMirrors.add(str); } } @@ -312,7 +317,8 @@ public class RepoXMLHandler extends DefaultHandler { } private void onRepoParsed() { - receiver.receiveRepo(repoName, repoDescription, repoSigningCert, repoMaxAge, repoVersion, repoTimestamp); + receiver.receiveRepo(repoName, repoDescription, repoSigningCert, repoMaxAge, repoVersion, + repoTimestamp, repoIcon, repoMirrors.toArray(new String[repoMirrors.size()])); } private void onRepoPushRequestParsed(RepoPushRequest repoPushRequest) { @@ -331,6 +337,7 @@ public class RepoXMLHandler extends DefaultHandler { repoName = cleanWhiteSpace(attributes.getValue("", "name")); repoDescription = cleanWhiteSpace(attributes.getValue("", "description")); repoTimestamp = parseLong(attributes.getValue("", "timestamp"), 0); + repoIcon = attributes.getValue("", "icon"); } else if (RepoPushRequest.INSTALL.equals(localName) || RepoPushRequest.UNINSTALL.equals(localName)) { if (repo.pushRequests == Repo.PUSH_REQUEST_ACCEPT_ALWAYS) { diff --git a/app/src/main/java/org/fdroid/fdroid/data/Apk.java b/app/src/main/java/org/fdroid/fdroid/data/Apk.java index d00250966..d1f75ff89 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java @@ -2,6 +2,7 @@ package org.fdroid.fdroid.data; import android.annotation.TargetApi; import android.content.ContentValues; +import android.content.pm.PackageInfo; import android.database.Cursor; import android.os.Build; import android.os.Parcel; @@ -73,6 +74,8 @@ public class Apk extends ValueObject implements Comparable, Parcelable { public String repoAddress; public String[] incompatibleReasons; + public String[] antiFeatures; + /** * The numeric primary key of the Metadata table, which is used to join apks. */ @@ -203,6 +206,9 @@ public class Apk extends ValueObject implements Comparable, Parcelable { case Cols.Repo.ADDRESS: repoAddress = cursor.getString(i); break; + case Cols.ANTI_FEATURES: + antiFeatures = Utils.parseCommaSeparatedString(cursor.getString(i)); + break; } } } @@ -303,6 +309,7 @@ public class Apk extends ValueObject implements Comparable, Parcelable { values.put(Cols.FEATURES, Utils.serializeCommaSeparatedString(features)); values.put(Cols.NATIVE_CODE, Utils.serializeCommaSeparatedString(nativecode)); values.put(Cols.INCOMPATIBLE_REASONS, Utils.serializeCommaSeparatedString(incompatibleReasons)); + values.put(Cols.ANTI_FEATURES, Utils.serializeCommaSeparatedString(antiFeatures)); values.put(Cols.IS_COMPATIBLE, compatible ? 1 : 0); return values; } @@ -349,6 +356,7 @@ public class Apk extends ValueObject implements Comparable, Parcelable { dest.writeInt(this.repoVersion); dest.writeString(this.repoAddress); dest.writeStringArray(this.incompatibleReasons); + dest.writeStringArray(this.antiFeatures); dest.writeLong(this.appId); } @@ -380,6 +388,7 @@ public class Apk extends ValueObject implements Comparable, Parcelable { this.repoVersion = in.readInt(); this.repoAddress = in.readString(); this.incompatibleReasons = in.createStringArray(); + this.antiFeatures = in.createStringArray(); this.appId = in.readLong(); } diff --git a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java index 14c10abb5..76a4f9312 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -76,6 +76,8 @@ class DBHelper extends SQLiteOpenHelper { + RepoTable.Cols.USERNAME + " string, " + RepoTable.Cols.PASSWORD + " string," + RepoTable.Cols.TIMESTAMP + " integer not null default 0, " + + RepoTable.Cols.ICON + " string, " + + RepoTable.Cols.MIRRORS + " string, " + RepoTable.Cols.PUSH_REQUESTS + " integer not null default " + Repo.PUSH_REQUEST_IGNORE + ");"; @@ -104,6 +106,7 @@ class DBHelper extends SQLiteOpenHelper { + ApkTable.Cols.ADDED_DATE + " string, " + ApkTable.Cols.IS_COMPATIBLE + " int not null, " + ApkTable.Cols.INCOMPATIBLE_REASONS + " text, " + + ApkTable.Cols.ANTI_FEATURES + " string, " + "PRIMARY KEY (" + ApkTable.Cols.APP_ID + ", " + ApkTable.Cols.VERSION_CODE + ", " + ApkTable.Cols.REPO_ID + ")" + ");"; @@ -178,7 +181,7 @@ class DBHelper extends SQLiteOpenHelper { + InstalledAppTable.Cols.HASH + " TEXT NOT NULL" + " );"; - protected static final int DB_VERSION = 65; + protected static final int DB_VERSION = 66; private final Context context; @@ -258,6 +261,27 @@ class DBHelper extends SQLiteOpenHelper { migrateToPackageTable(db, oldVersion); addObbFiles(db, oldVersion); addCategoryTables(db, oldVersion); + addIndexV1Fields(db, oldVersion); + } + + private void addIndexV1Fields(SQLiteDatabase db, int oldVersion) { + if (oldVersion >= 66) { + return; + } + if (!columnExists(db, Schema.RepoTable.NAME, RepoTable.Cols.ICON)) { + Utils.debugLog(TAG, "Adding " + RepoTable.Cols.ICON + " field to " + RepoTable.NAME + " table in db."); + db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.ICON + " string;"); + } + + if (!columnExists(db, RepoTable.NAME, RepoTable.Cols.MIRRORS)) { + Utils.debugLog(TAG, "Adding " + RepoTable.Cols.MIRRORS + " field to " + RepoTable.NAME + " table in db."); + db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.MIRRORS + " string;"); + } + + if (!columnExists(db, ApkTable.NAME, ApkTable.Cols.ANTI_FEATURES)) { + Utils.debugLog(TAG, "Adding " + ApkTable.Cols.ANTI_FEATURES + " field to " + ApkTable.NAME + " table in db."); + db.execSQL("alter table " + ApkTable.NAME + " add column " + ApkTable.Cols.ANTI_FEATURES + " string;"); + } } /** diff --git a/app/src/main/java/org/fdroid/fdroid/data/Repo.java b/app/src/main/java/org/fdroid/fdroid/data/Repo.java index fa8289bdf..589c5d0ce 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Repo.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Repo.java @@ -47,6 +47,7 @@ public class Repo extends ValueObject { public String address; public String name; public String description; + public String icon; /** index version, i.e. what fdroidserver built it - 0 if not specified */ public int version; public boolean inuse; @@ -71,6 +72,9 @@ public class Repo extends ValueObject { /** When the signed repo index was generated, used to protect against replay attacks */ public long timestamp; + /** Official mirrors of this repo, considered automatically interchangeable */ + public String[] mirrors; + /** How to treat push requests included in this repo's index XML */ public int pushRequests = PUSH_REQUEST_IGNORE; @@ -131,6 +135,12 @@ public class Repo extends ValueObject { case Cols.TIMESTAMP: timestamp = cursor.getLong(i); break; + case Cols.ICON: + icon = cursor.getString(i); + break; + case Cols.MIRRORS: + mirrors = Utils.parseCommaSeparatedString(cursor.getString(i)); + break; case Cols.PUSH_REQUESTS: pushRequests = cursor.getInt(i); break; @@ -257,6 +267,14 @@ public class Repo extends ValueObject { timestamp = toInt(values.getAsInteger(Cols.TIMESTAMP)); } + if (values.containsKey(Cols.ICON)) { + icon = values.getAsString(Cols.ICON); + } + + if (values.containsKey(Cols.MIRRORS)) { + mirrors = Utils.parseCommaSeparatedString(values.getAsString(Cols.MIRRORS)); + } + if (values.containsKey(Cols.PUSH_REQUESTS)) { pushRequests = toInt(values.getAsInteger(Cols.PUSH_REQUESTS)); } diff --git a/app/src/main/java/org/fdroid/fdroid/data/Schema.java b/app/src/main/java/org/fdroid/fdroid/data/Schema.java index 54c44c407..6c0491fba 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Schema.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Schema.java @@ -240,6 +240,7 @@ public interface Schema { String ADDED_DATE = "added"; String IS_COMPATIBLE = "compatible"; String INCOMPATIBLE_REASONS = "incompatibleReasons"; + String ANTI_FEATURES = "antiFeatures"; interface Repo { String VERSION = "repoVersion"; @@ -258,7 +259,7 @@ public interface Schema { SIZE, SIGNATURE, SOURCE_NAME, MIN_SDK_VERSION, TARGET_SDK_VERSION, MAX_SDK_VERSION, OBB_MAIN_FILE, OBB_MAIN_FILE_SHA256, OBB_PATCH_FILE, OBB_PATCH_FILE_SHA256, REQUESTED_PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE, - IS_COMPATIBLE, INCOMPATIBLE_REASONS, + IS_COMPATIBLE, INCOMPATIBLE_REASONS, ANTI_FEATURES, }; /** @@ -270,6 +271,7 @@ public interface Schema { OBB_MAIN_FILE, OBB_MAIN_FILE_SHA256, OBB_PATCH_FILE, OBB_PATCH_FILE_SHA256, REQUESTED_PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE, IS_COMPATIBLE, Repo.VERSION, Repo.ADDRESS, INCOMPATIBLE_REASONS, + ANTI_FEATURES, }; } } @@ -295,12 +297,14 @@ public interface Schema { String USERNAME = "username"; String PASSWORD = "password"; String TIMESTAMP = "timestamp"; + String ICON = "icon"; + String MIRRORS = "mirrors"; String PUSH_REQUESTS = "pushRequests"; String[] ALL = { _ID, ADDRESS, NAME, DESCRIPTION, IN_USE, PRIORITY, SIGNING_CERT, FINGERPRINT, MAX_AGE, LAST_UPDATED, LAST_ETAG, VERSION, IS_SWAP, - USERNAME, PASSWORD, TIMESTAMP, PUSH_REQUESTS, + USERNAME, PASSWORD, TIMESTAMP, ICON, MIRRORS, PUSH_REQUESTS, }; } } diff --git a/app/src/testShared/java/org/fdroid/fdroid/mock/RepoDetails.java b/app/src/testShared/java/org/fdroid/fdroid/mock/RepoDetails.java index 4371bbaa4..5d8428a15 100644 --- a/app/src/testShared/java/org/fdroid/fdroid/mock/RepoDetails.java +++ b/app/src/testShared/java/org/fdroid/fdroid/mock/RepoDetails.java @@ -31,19 +31,24 @@ public class RepoDetails implements RepoXMLHandler.IndexReceiver { public int maxAge; public int version; public long timestamp; + public String icon; + public String[] mirrors; public List apks = new ArrayList<>(); public List apps = new ArrayList<>(); public List repoPushRequestList = new ArrayList<>(); @Override - public void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp) { + public void receiveRepo(String name, String description, String signingCert, int maxage, + int version, long timestamp, String icon, String[] mirrors) { this.name = name; this.description = description; this.signingCert = signingCert; this.maxAge = maxage; this.version = version; this.timestamp = timestamp; + this.icon = icon; + this.mirrors = mirrors; } @Override