From cd9582c9902dd4ac9218acfd69872f3eebcd3d93 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 28 Jun 2016 00:07:50 +0200 Subject: [PATCH] support "APK Extension" files aka .obb for large apps and games OBB files are used in apps that need more than 100 megs to work well. This is apps like MAPS.ME or games that put map info, media, etc. into the OBB file. Also, OBB files provide a mechanism to deliver large data blobs that do not need to be part of the APK. For example, a game's assets do not need to change often, so they can be shipped as an OBB, then APK updates do not need to include all those assets for each update. https://developer.android.com/google/play/expansion-files.html --- .../org/fdroid/fdroid/RepoXMLHandler.java | 13 +++ .../main/java/org/fdroid/fdroid/data/Apk.java | 94 ++++++++++++++++++- .../main/java/org/fdroid/fdroid/data/App.java | 43 ++++++++- .../java/org/fdroid/fdroid/data/DBHelper.java | 32 ++++++- .../java/org/fdroid/fdroid/data/Schema.java | 6 ++ .../fdroid/updater/RepoXMLHandlerTest.java | 38 ++++++++ .../main.1101613.obb.main.twoversions.obb | 1 + .../main.1101615.obb.main.twoversions.obb | 1 + .../main.1434483388.obb.main.oldversion.obb | 1 + .../main.1619.obb.mainpatch.current.obb | 1 + app/src/test/resources/obbIndex.xml | 1 + .../patch.1619.obb.mainpatch.current.obb | 1 + 12 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 app/src/test/resources/main.1101613.obb.main.twoversions.obb create mode 100644 app/src/test/resources/main.1101615.obb.main.twoversions.obb create mode 100644 app/src/test/resources/main.1434483388.obb.main.oldversion.obb create mode 100644 app/src/test/resources/main.1619.obb.mainpatch.current.obb create mode 100644 app/src/test/resources/obbIndex.xml create mode 100644 app/src/test/resources/patch.1619.obb.mainpatch.current.obb diff --git a/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java b/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java index 984b99de7..bb7d53580 100644 --- a/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java +++ b/app/src/main/java/org/fdroid/fdroid/RepoXMLHandler.java @@ -26,6 +26,7 @@ import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoPushRequest; +import org.fdroid.fdroid.data.Schema.ApkTable; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; @@ -141,6 +142,18 @@ public class RepoXMLHandler extends DefaultHandler { curapk.maxSdkVersion = Apk.SDK_VERSION_MAX_VALUE; } break; + case ApkTable.Cols.OBB_MAIN_FILE: + curapk.obbMainFile = str; + break; + case ApkTable.Cols.OBB_MAIN_FILE_SHA256: + curapk.obbMainFileSha256 = str; + break; + case ApkTable.Cols.OBB_PATCH_FILE: + curapk.obbPatchFile = str; + break; + case ApkTable.Cols.OBB_PATCH_FILE_SHA256: + curapk.obbPatchFileSha256 = str; + break; case "added": curapk.added = Utils.parseDate(str, null); break; 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 5580196ca..592644699 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java @@ -10,6 +10,7 @@ import android.os.Parcelable; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Schema.ApkTable.Cols; +import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -30,6 +31,10 @@ public class Apk extends ValueObject implements Comparable, Parcelable { public int minSdkVersion = SDK_VERSION_MIN_VALUE; // 0 if unknown public int targetSdkVersion = SDK_VERSION_MIN_VALUE; // 0 if unknown public int maxSdkVersion = SDK_VERSION_MAX_VALUE; // "infinity" if not set + public String obbMainFile; + public String obbMainFileSha256; + public String obbPatchFile; + public String obbPatchFileSha256; public Date added; public String[] permissions; // null if empty or // unknown @@ -106,6 +111,18 @@ public class Apk extends ValueObject implements Comparable, Parcelable { case Cols.MAX_SDK_VERSION: maxSdkVersion = cursor.getInt(i); break; + case Cols.OBB_MAIN_FILE: + obbMainFile = cursor.getString(i); + break; + case Cols.OBB_MAIN_FILE_SHA256: + obbMainFileSha256 = cursor.getString(i); + break; + case Cols.OBB_PATCH_FILE: + obbPatchFile = cursor.getString(i); + break; + case Cols.OBB_PATCH_FILE_SHA256: + obbPatchFileSha256 = cursor.getString(i); + break; case Cols.NAME: apkName = cursor.getString(i); break; @@ -146,13 +163,73 @@ public class Apk extends ValueObject implements Comparable, Parcelable { } } - public String getUrl() { + private void checkRepoAddress() { if (repoAddress == null || apkName == null) { throw new IllegalStateException("Apk needs to have both Schema.ApkTable.Cols.REPO_ADDRESS and Schema.ApkTable.Cols.NAME set in order to calculate URL."); } + } + + public String getUrl() { + checkRepoAddress(); return repoAddress + "/" + apkName.replace(" ", "%20"); } + /** + * Get the URL to download the main expansion file, the primary + * expansion file for additional resources required by your application. + * The filename will always have the format: + * "main.versionCode.packageName.obb" + * + * @return a URL to download the OBB file that matches this APK + * @see #getPatchObbUrl() + * @see APK Expansion Files + */ + public String getMainObbUrl() { + if (repoAddress == null || obbMainFile == null) { + return null; + } + checkRepoAddress(); + return repoAddress + "/" + obbMainFile; + } + + /** + * Get the URL to download the optional patch expansion file, which + * is intended for small updates to the main expansion file. + * The filename will always have the format: + * "patch.versionCode.packageName.obb" + * + * @return a URL to download the OBB file that matches this APK + * @see #getMainObbUrl() + * @see APK Expansion Files + */ + public String getPatchObbUrl() { + if (repoAddress == null || obbPatchFile == null) { + return null; + } + checkRepoAddress(); + return repoAddress + "/" + obbPatchFile; + } + + /** + * Get the local {@link File} to the "main" OBB file. + */ + public File getMainObbFile() { + if (obbMainFile == null) { + return null; + } + return new File(App.getObbDir(packageName), obbMainFile); + } + + /** + * Get the local {@link File} to the "patch" OBB file. + */ + public File getPatchObbFile() { + if (obbPatchFile == null) { + return null; + } + return new File(App.getObbDir(packageName), obbPatchFile); + } + public ArrayList getFullPermissionList() { if (this.permissions == null) { return new ArrayList<>(); @@ -180,7 +257,8 @@ public class Apk extends ValueObject implements Comparable, Parcelable { * FDroid just includes the constant name in the apk list, so we prefix it * with "android.permission." * - * see https://gitlab.com/fdroid/fdroidserver/blob/master/fdroidserver/update.py#L535# + * @see + * More info into index - size, permissions, features, sdk version */ private static String fdroidToAndroidPermission(String permission) { if (!permission.contains(".")) { @@ -210,6 +288,10 @@ public class Apk extends ValueObject implements Comparable, Parcelable { values.put(Cols.MIN_SDK_VERSION, minSdkVersion); values.put(Cols.TARGET_SDK_VERSION, targetSdkVersion); values.put(Cols.MAX_SDK_VERSION, maxSdkVersion); + values.put(Cols.OBB_MAIN_FILE, obbMainFile); + values.put(Cols.OBB_MAIN_FILE_SHA256, obbMainFileSha256); + values.put(Cols.OBB_PATCH_FILE, obbPatchFile); + values.put(Cols.OBB_PATCH_FILE_SHA256, obbPatchFileSha256); values.put(Cols.ADDED_DATE, Utils.formatDate(added, "")); values.put(Cols.PERMISSIONS, Utils.serializeCommaSeparatedString(permissions)); values.put(Cols.FEATURES, Utils.serializeCommaSeparatedString(features)); @@ -245,6 +327,10 @@ public class Apk extends ValueObject implements Comparable, Parcelable { dest.writeInt(this.minSdkVersion); dest.writeInt(this.targetSdkVersion); dest.writeInt(this.maxSdkVersion); + dest.writeString(this.obbMainFile); + dest.writeString(this.obbMainFileSha256); + dest.writeString(this.obbPatchFile); + dest.writeString(this.obbPatchFileSha256); dest.writeLong(this.added != null ? this.added.getTime() : -1); dest.writeStringArray(this.permissions); dest.writeStringArray(this.features); @@ -271,6 +357,10 @@ public class Apk extends ValueObject implements Comparable, Parcelable { this.minSdkVersion = in.readInt(); this.targetSdkVersion = in.readInt(); this.maxSdkVersion = in.readInt(); + this.obbMainFile = in.readString(); + this.obbMainFileSha256 = in.readString(); + this.obbPatchFile = in.readString(); + this.obbPatchFileSha256 = in.readString(); long tmpAdded = in.readLong(); this.added = tmpAdded == -1 ? null : new Date(tmpAdded); this.permissions = in.createStringArray(); diff --git a/app/src/main/java/org/fdroid/fdroid/data/App.java b/app/src/main/java/org/fdroid/fdroid/data/App.java index 611072bf9..e7d96ba1d 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -9,22 +9,27 @@ import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.XmlResourceParser; import android.database.Cursor; +import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; +import org.apache.commons.io.filefilter.RegexFileFilter; import org.fdroid.fdroid.AppFilter; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Utils; +import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; +import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import java.util.HashSet; @@ -33,8 +38,6 @@ import java.util.jar.JarFile; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols; - public class App extends ValueObject implements Comparable, Parcelable { private static final String TAG = "App"; @@ -272,6 +275,17 @@ public class App extends ValueObject implements Comparable, Parcelable { initApkFromApkFile(context, this.installedApk, packageInfo, apkFile); } + /** + * Get the directory where APK Expansion Files aka OBB files are stored for the app as + * specified by {@code packageName}. + * + * @see APK Expansion Files + */ + public static File getObbDir(String packageName) { + return new File(Environment.getExternalStorageDirectory().getAbsolutePath() + + "/Android/obb/" + packageName); + } + private void setFromPackageInfo(PackageManager pm, PackageInfo packageInfo) { this.packageName = packageInfo.packageName; @@ -324,6 +338,29 @@ public class App extends ValueObject implements Comparable, Parcelable { initInstalledApk(context, apk, packageInfo, apkFile); } + public static void initInstalledObbFiles(Apk apk) { + File obbdir = getObbDir(apk.packageName); + FileFilter filter = new RegexFileFilter("(main|patch)\\.[0-9-][0-9]*\\." + apk.packageName + "\\.obb"); + File[] files = obbdir.listFiles(filter); + if (files == null) { + return; + } + Arrays.sort(files); + for (File f : files) { + String filename = f.getName(); + String[] segments = filename.split("\\."); + if (Integer.parseInt(segments[1]) <= apk.versionCode) { + if ("main".equals(segments[0])) { + apk.obbMainFile = filename; + apk.obbMainFileSha256 = Utils.getBinaryHash(f, apk.hashType); + } else if ("patch".equals(segments[0])) { + apk.obbPatchFile = filename; + apk.obbPatchFileSha256 = Utils.getBinaryHash(f, apk.hashType); + } + } + } + } + private void initInstalledApk(Context context, Apk apk, PackageInfo packageInfo, SanitizedFile apkFile) throws IOException, CertificateEncodingException { apk.compatible = true; @@ -339,6 +376,8 @@ public class App extends ValueObject implements Comparable, Parcelable { apk.apkName = apk.packageName + "_" + apk.versionCode + ".apk"; apk.installedFile = apkFile; + initInstalledObbFiles(apk); + JarFile apkJar = new JarFile(apkFile); HashSet abis = new HashSet<>(3); Pattern pattern = Pattern.compile("^lib/([a-z0-9-]+)/.*"); 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 9c238295d..51270d448 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -92,6 +92,10 @@ class DBHelper extends SQLiteOpenHelper { + ApkTable.Cols.MIN_SDK_VERSION + " integer, " + ApkTable.Cols.TARGET_SDK_VERSION + " integer, " + ApkTable.Cols.MAX_SDK_VERSION + " integer, " + + ApkTable.Cols.OBB_MAIN_FILE + " string, " + + ApkTable.Cols.OBB_MAIN_FILE_SHA256 + " string, " + + ApkTable.Cols.OBB_PATCH_FILE + " string, " + + ApkTable.Cols.OBB_PATCH_FILE_SHA256 + " string, " + ApkTable.Cols.PERMISSIONS + " string, " + ApkTable.Cols.FEATURES + " string, " + ApkTable.Cols.NATIVE_CODE + " string, " @@ -154,7 +158,7 @@ class DBHelper extends SQLiteOpenHelper { + " );"; private static final String DROP_TABLE_INSTALLED_APP = "DROP TABLE " + InstalledAppTable.NAME + ";"; - private static final int DB_VERSION = 63; + private static final int DB_VERSION = 64; private final Context context; @@ -354,6 +358,24 @@ class DBHelper extends SQLiteOpenHelper { lowerCaseApkHashes(db, oldVersion); supportRepoPushRequests(db, oldVersion); migrateToPackageTable(db, oldVersion); + addObbFiles(db, oldVersion); + } + + private void addObbFiles(SQLiteDatabase db, int oldVersion) { + if (oldVersion >= 64) { + return; + } + Utils.debugLog(TAG, "Adding " + ApkTable.Cols.OBB_MAIN_FILE + + ", " + ApkTable.Cols.OBB_PATCH_FILE + + ", and hash columns to " + ApkTable.NAME); + db.execSQL("alter table " + ApkTable.NAME + " add column " + + ApkTable.Cols.OBB_MAIN_FILE + " string"); + db.execSQL("alter table " + ApkTable.NAME + " add column " + + ApkTable.Cols.OBB_MAIN_FILE_SHA256 + " string"); + db.execSQL("alter table " + ApkTable.NAME + " add column " + + ApkTable.Cols.OBB_PATCH_FILE + " string"); + db.execSQL("alter table " + ApkTable.NAME + " add column " + + ApkTable.Cols.OBB_PATCH_FILE_SHA256 + " string"); } private void migrateToPackageTable(SQLiteDatabase db, int oldVersion) { @@ -477,6 +499,10 @@ class DBHelper extends SQLiteOpenHelper { + ApkTable.Cols.MIN_SDK_VERSION + " integer, " + ApkTable.Cols.TARGET_SDK_VERSION + " integer, " + ApkTable.Cols.MAX_SDK_VERSION + " integer, " + + ApkTable.Cols.OBB_MAIN_FILE + " string, " + + ApkTable.Cols.OBB_MAIN_FILE_SHA256 + " string, " + + ApkTable.Cols.OBB_PATCH_FILE + " string, " + + ApkTable.Cols.OBB_PATCH_FILE_SHA256 + " string, " + ApkTable.Cols.PERMISSIONS + " string, " + ApkTable.Cols.FEATURES + " string, " + ApkTable.Cols.NATIVE_CODE + " string, " @@ -502,6 +528,10 @@ class DBHelper extends SQLiteOpenHelper { ApkTable.Cols.MIN_SDK_VERSION, ApkTable.Cols.TARGET_SDK_VERSION, ApkTable.Cols.MAX_SDK_VERSION, + ApkTable.Cols.OBB_MAIN_FILE, + ApkTable.Cols.OBB_MAIN_FILE_SHA256, + ApkTable.Cols.OBB_PATCH_FILE, + ApkTable.Cols.OBB_PATCH_FILE_SHA256, ApkTable.Cols.PERMISSIONS, ApkTable.Cols.FEATURES, ApkTable.Cols.NATIVE_CODE, 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 f14cb1484..b910c107f 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Schema.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Schema.java @@ -165,6 +165,10 @@ public interface Schema { String MIN_SDK_VERSION = "minSdkVersion"; String TARGET_SDK_VERSION = "targetSdkVersion"; String MAX_SDK_VERSION = "maxSdkVersion"; + String OBB_MAIN_FILE = "obbMainFile"; + String OBB_MAIN_FILE_SHA256 = "obbMainFileSha256"; + String OBB_PATCH_FILE = "obbPatchFile"; + String OBB_PATCH_FILE_SHA256 = "obbPatchFileSha256"; String PERMISSIONS = "permissions"; String FEATURES = "features"; String NATIVE_CODE = "nativecode"; @@ -188,6 +192,7 @@ public interface Schema { String[] ALL_COLS = { APP_ID, VERSION_NAME, REPO_ID, HASH, VERSION_CODE, NAME, 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, PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE, IS_COMPATIBLE, INCOMPATIBLE_REASONS, }; @@ -198,6 +203,7 @@ public interface Schema { String[] ALL = { _ID, APP_ID, Package.PACKAGE_NAME, VERSION_NAME, REPO_ID, HASH, VERSION_CODE, NAME, 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, PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE, IS_COMPATIBLE, Repo.VERSION, Repo.ADDRESS, INCOMPATIBLE_REASONS, }; diff --git a/app/src/test/java/org/fdroid/fdroid/updater/RepoXMLHandlerTest.java b/app/src/test/java/org/fdroid/fdroid/updater/RepoXMLHandlerTest.java index 12751a40a..193c8dc23 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/RepoXMLHandlerTest.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/RepoXMLHandlerTest.java @@ -26,6 +26,7 @@ import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; +import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.RepoXMLHandler; import org.fdroid.fdroid.data.Apk; @@ -33,15 +34,18 @@ import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoPushRequest; import org.fdroid.fdroid.mock.MockRepo; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowLog; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import java.io.BufferedInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -70,6 +74,32 @@ public class RepoXMLHandlerTest { private static final String FAKE_SIGNING_CERT = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"; + @Before + public void setUp() { + ShadowLog.stream = System.out; + } + + @Test + public void testObbIndex() throws IOException { + writeResourceToObbDir("main.1101613.obb.main.twoversions.obb"); + writeResourceToObbDir("main.1101615.obb.main.twoversions.obb"); + writeResourceToObbDir("main.1434483388.obb.main.oldversion.obb"); + writeResourceToObbDir("main.1619.obb.mainpatch.current.obb"); + writeResourceToObbDir("patch.1619.obb.mainpatch.current.obb"); + RepoDetails actualDetails = getFromFile("obbIndex.xml"); + for (Apk indexApk : actualDetails.apks) { + Apk localApk = new Apk(); + localApk.packageName = indexApk.packageName; + localApk.versionCode = indexApk.versionCode; + localApk.hashType = indexApk.hashType; + App.initInstalledObbFiles(localApk); + assertEquals(indexApk.obbMainFile, localApk.obbMainFile); + assertEquals(indexApk.obbMainFileSha256, localApk.obbMainFileSha256); + assertEquals(indexApk.obbPatchFile, localApk.obbPatchFile); + assertEquals(indexApk.obbPatchFileSha256, localApk.obbPatchFileSha256); + } + } + @Test public void testSimpleIndex() { Repo expectedRepo = new Repo(); @@ -871,4 +901,12 @@ public class RepoXMLHandlerTest { } } + private void writeResourceToObbDir(String assetName) throws IOException { + InputStream input = getClass().getClassLoader().getResourceAsStream(assetName); + String packageName = assetName.substring(assetName.indexOf("obb"), + assetName.lastIndexOf('.')); + File f = new File(App.getObbDir(packageName), assetName); + FileUtils.copyToFile(input, f); + input.close(); + } } diff --git a/app/src/test/resources/main.1101613.obb.main.twoversions.obb b/app/src/test/resources/main.1101613.obb.main.twoversions.obb new file mode 100644 index 000000000..421376db9 --- /dev/null +++ b/app/src/test/resources/main.1101613.obb.main.twoversions.obb @@ -0,0 +1 @@ +dummy diff --git a/app/src/test/resources/main.1101615.obb.main.twoversions.obb b/app/src/test/resources/main.1101615.obb.main.twoversions.obb new file mode 100644 index 000000000..421376db9 --- /dev/null +++ b/app/src/test/resources/main.1101615.obb.main.twoversions.obb @@ -0,0 +1 @@ +dummy diff --git a/app/src/test/resources/main.1434483388.obb.main.oldversion.obb b/app/src/test/resources/main.1434483388.obb.main.oldversion.obb new file mode 100644 index 000000000..421376db9 --- /dev/null +++ b/app/src/test/resources/main.1434483388.obb.main.oldversion.obb @@ -0,0 +1 @@ +dummy diff --git a/app/src/test/resources/main.1619.obb.mainpatch.current.obb b/app/src/test/resources/main.1619.obb.mainpatch.current.obb new file mode 100644 index 000000000..421376db9 --- /dev/null +++ b/app/src/test/resources/main.1619.obb.mainpatch.current.obb @@ -0,0 +1 @@ +dummy diff --git a/app/src/test/resources/obbIndex.xml b/app/src/test/resources/obbIndex.xml new file mode 100644 index 000000000..197d0eddf --- /dev/null +++ b/app/src/test/resources/obbIndex.xml @@ -0,0 +1 @@ +This is a repository of apps to be used with F-Droid. Applications in this repository are either official binaries built by the original application developers, or are binaries built from source by the admin of f-droid.org using the tools on https://gitlab.com/u/fdroid. obb.main.oldversion2016-06-272016-06-27OBB Main Old Versionobb.main.oldversion.1444412523.png<p>No description available</p>GPLv3DevelopmentDevelopmenthttps://github.com/eighthave/urzip1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk999999990.11444412523obb.main.oldversion_1444412523.apk7562a36c9e2b38013b96663cf41f0f290dc7a248a81befa8d89e14f390c94c7fb4964fd759edaa54e65bb476d027688011470418main.1434483388.obb.main.oldversion.obbd3eb539a556352f3f47881d71fb0e5777b2f3e9a4251d283c18c67ce996774b72016-06-27obb.main.twoversions2016-06-272016-06-27OBB Main Two Versionsobb.main.twoversions.1101617.png<p>No description available</p>GPLv3DevelopmentDevelopmenthttps://github.com/eighthave/urzip1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk999999990.11101617obb.main.twoversions_1101617.apk9bc74566f089ef030ac33e7fbd99d92f1a38f363fb499fed138d9e7b774e821cb4964fd759edaa54e65bb476d027688011481418main.1101615.obb.main.twoversions.obbd3eb539a556352f3f47881d71fb0e5777b2f3e9a4251d283c18c67ce996774b72016-06-270.11101615obb.main.twoversions_1101615.apk7b0b7b9ba248e15751a16e3a0e01e1e24cbb673686c38422030cb75d5c33f0bbb4964fd759edaa54e65bb476d027688011480418main.1101615.obb.main.twoversions.obbd3eb539a556352f3f47881d71fb0e5777b2f3e9a4251d283c18c67ce996774b72016-06-270.11101613obb.main.twoversions_1101613.apkcce97a52ff18d843185be7f22ecb1a557c36b7a9f8ba07a8be94e328e00b35dcb4964fd759edaa54e65bb476d027688011477418main.1101613.obb.main.twoversions.obbd3eb539a556352f3f47881d71fb0e5777b2f3e9a4251d283c18c67ce996774b72016-06-27obb.mainpatch.current2016-06-272016-06-27OBB Main/Patch Currentobb.mainpatch.current.1619.png<p>No description available</p>GPLv3DevelopmentDevelopmenthttps://github.com/eighthave/urzip1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk999999990.11619obb.mainpatch.current_1619.apkeda5fc3ecfdac3252717e36bdbc9820865baeef162264af9ba5db7364f0e7a0cb4964fd759edaa54e65bb476d027688011479418main.1619.obb.mainpatch.current.obbd3eb539a556352f3f47881d71fb0e5777b2f3e9a4251d283c18c67ce996774b7patch.1619.obb.mainpatch.current.obbd3eb539a556352f3f47881d71fb0e5777b2f3e9a4251d283c18c67ce996774b72016-06-27info.guardianproject.urzip2016-06-272016-06-27urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234一个实用工具,获取已安装在您的设备上的应用的有关信息info.guardianproject.urzip.100.png<p>It’s Urzip 是一个获得已安装 APK 相关信息的实用工具。它从您的设备上已安装的所有应用开始,一键触摸即可显示 APK 的指纹,并且提供到达 virustotal.com 和 androidobservatory.org 的快捷链接,让您方便地了解特定 APK 的档案。它还可以让您导出签名证书和生成 ApkSignaturePin Pin 文件供 TrustedIntents 库使用。</p><p>★ Urzip 支持下列语言: Deutsch, English, español, suomi, 日本語, 한국어, Norsk, português (Portugal), Русский, Slovenščina, Türkçe 没看到您的语言?帮忙翻译本应用吧: https://www.transifex.com/projects/p/urzip</p><p>★ 致用户:我们还缺少你喜欢的功能?发现了一个 bug?请告诉我们!我们乐于听取您的意见。请发送电子邮件至: support@guardianproject.info 或者加入我们的聊天室 https://guardianproject.info/contact</p>GPLv3Development,GuardianProjectDevelopmenthttps://dev.guardianproject.info/projects/urziphttps://github.com/guardianproject/urziphttps://dev.guardianproject.info/projects/urzip/issues1Fi5xUHiAPRKxHvyUGVFGt9extBe8Srdbk99999990.1100urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234.apk15c0ec72c74a3791f42cdb43c57df0fb11a4dbb656851bbb8cf05b26a8372789b4964fd759edaa54e65bb476d0276880114714182016-06-27 \ No newline at end of file diff --git a/app/src/test/resources/patch.1619.obb.mainpatch.current.obb b/app/src/test/resources/patch.1619.obb.mainpatch.current.obb new file mode 100644 index 000000000..421376db9 --- /dev/null +++ b/app/src/test/resources/patch.1619.obb.mainpatch.current.obb @@ -0,0 +1 @@ +dummy