diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/SwapService.java b/app/src/full/java/org/fdroid/fdroid/nearby/SwapService.java index 358cde6f0..2549d41fc 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/SwapService.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/SwapService.java @@ -33,6 +33,7 @@ import org.fdroid.index.IndexParserKt; import org.fdroid.index.SigningException; import org.fdroid.index.v1.IndexV1; import org.fdroid.index.v1.IndexV1Verifier; +import org.fdroid.index.v2.FileV2; import java.io.File; import java.io.IOException; @@ -130,11 +131,12 @@ public class SwapService extends Service { private void updateRepo(@NonNull Peer peer, Repository repo) throws IOException, InterruptedException, SigningException { Uri uri = Uri.parse(repo.getAddress()).buildUpon().appendPath("index-v1.jar").build(); + FileV2 indexFile = FileV2.fromPath("/index-v1.jar"); File swapJarFile = File.createTempFile("swap", "", getApplicationContext().getCacheDir()); try { Downloader downloader = - DownloaderFactory.INSTANCE.createWithTryFirstMirror(repo, uri, swapJarFile); + DownloaderFactory.INSTANCE.createWithTryFirstMirror(repo, uri, indexFile, swapJarFile); downloader.download(); IndexV1Verifier verifier = new IndexV1Verifier(swapJarFile, null, peer.getFingerprint()); Pair pair = verifier.getStreamAndVerify(inputStream -> diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/SwapSuccessView.java b/app/src/full/java/org/fdroid/fdroid/nearby/SwapSuccessView.java index 8dac0a7d8..b20a5ca18 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/SwapSuccessView.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/SwapSuccessView.java @@ -40,6 +40,8 @@ import org.fdroid.index.v1.AppV1; import org.fdroid.index.v1.IndexV1; import org.fdroid.index.v1.PackageV1; import org.fdroid.index.v1.PermissionV1; +import org.fdroid.index.v2.FileV1; +import org.fdroid.index.v2.FileV2; import java.util.ArrayList; import java.util.HashMap; @@ -98,7 +100,7 @@ public class SwapSuccessView extends SwapView { App app = new App(); app.name = a.getName(); app.packageName = a.getPackageName(); - app.iconUrl = "icons/" + a.getIcon(); + app.iconFile = FileV2.fromPath("icons/" + a.getIcon()); try { PackageInfo packageInfo = getContext().getPackageManager().getPackageInfo(app.packageName, 0); app.installedVersionCode = packageInfo.versionCode; @@ -115,9 +117,7 @@ public class SwapSuccessView extends SwapView { apk.versionCode = packageV1.getVersionCode(); } apk.versionName = packageV1.getVersionName(); - apk.apkName = packageV1.getApkName(); - apk.hashType = packageV1.getHashType(); - apk.hash = packageV1.getHash(); + apk.apkFile = new FileV1("/" + packageV1.getApkName(), packageV1.getHash(), packageV1.getSize(), null); ArrayList permissions = new ArrayList<>(packageV1.getUsesPermission().size()); for (PermissionV1 perm : packageV1.getUsesPermission()) { @@ -313,9 +313,8 @@ public class SwapSuccessView extends SwapView { nameView.setText(app.name); } - String path = app.getIconPath(getContext()); Glide.with(iconView.getContext()) - .load(Utils.getDownloadRequest(repo, path)) + .load(Utils.getDownloadRequest(repo, app.iconFile)) .apply(Utils.getAlwaysShowIconRequestOptions()) .into(iconView); diff --git a/app/src/main/java/org/fdroid/fdroid/AppUpdateStatusManager.java b/app/src/main/java/org/fdroid/fdroid/AppUpdateStatusManager.java index 670478b72..ccc89d7c5 100644 --- a/app/src/main/java/org/fdroid/fdroid/AppUpdateStatusManager.java +++ b/app/src/main/java/org/fdroid/fdroid/AppUpdateStatusManager.java @@ -294,12 +294,13 @@ public final class AppUpdateStatusManager { } private void updateApkInternal(@NonNull AppUpdateStatus entry, @NonNull Status status, PendingIntent intent) { + String apkName = entry.apk.getApkPath(); if (status == Status.UpdateAvailable && entry.status.ordinal() > status.ordinal()) { - Utils.debugLog(LOGTAG, "Not updating APK " + entry.apk.apkName + " state to " + status.name()); + Utils.debugLog(LOGTAG, "Not updating APK " + apkName + " state to " + status.name()); // If we have this entry in a more advanced state already, don't downgrade it return; } else { - Utils.debugLog(LOGTAG, "Update APK " + entry.apk.apkName + " state to " + status.name()); + Utils.debugLog(LOGTAG, "Update APK " + apkName + " state to " + status.name()); } boolean isStatusUpdate = entry.status != status; entry.status = status; @@ -315,7 +316,8 @@ public final class AppUpdateStatusManager { } private void addApkInternal(@NonNull App app, @NonNull Apk apk, @NonNull Status status, PendingIntent intent) { - Utils.debugLog(LOGTAG, "Add APK " + apk.apkName + " with state " + status.name()); + String apkName = apk.getApkPath(); + Utils.debugLog(LOGTAG, "Add APK " + apkName + " with state " + status.name()); AppUpdateStatus entry = createAppEntry(app, apk, status, intent); setEntryContentIntentIfEmpty(entry); appMapping.put(entry.getCanonicalUrl(), entry); @@ -463,7 +465,7 @@ public final class AppUpdateStatusManager { InstallManagerService.removePendingInstall(context, canonicalUrl); AppUpdateStatus entry = appMapping.remove(canonicalUrl); if (entry != null) { - Utils.debugLog(LOGTAG, "Remove APK " + entry.apk.apkName); + Utils.debugLog(LOGTAG, "Remove APK " + entry.apk.getApkPath()); notifyRemove(entry); } } @@ -473,7 +475,7 @@ public final class AppUpdateStatusManager { synchronized (appMapping) { AppUpdateStatus entry = appMapping.get(canonicalUrl); if (entry != null) { - Utils.debugLog(LOGTAG, "Refresh APK " + entry.apk.apkName); + Utils.debugLog(LOGTAG, "Refresh APK " + entry.apk.getApkPath()); notifyChange(entry, true); } } diff --git a/app/src/main/java/org/fdroid/fdroid/NotificationHelper.java b/app/src/main/java/org/fdroid/fdroid/NotificationHelper.java index 091d61687..d8b1203f4 100644 --- a/app/src/main/java/org/fdroid/fdroid/NotificationHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/NotificationHelper.java @@ -548,7 +548,7 @@ public class NotificationHelper { } }); } else { - App.loadBitmapWithGlide(context, entry.app.repoId, entry.app.getIconPath(context)) + App.loadBitmapWithGlide(context, entry.app.repoId, entry.app.getIconPath()) .into(new CustomTarget() { @Override public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 76f17edc5..1567e20af 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -52,6 +52,7 @@ import com.google.zxing.BarcodeFormat; import com.google.zxing.encode.Contents; import com.google.zxing.encode.QRCodeEncoder; +import org.fdroid.IndexFile; import org.fdroid.database.AppOverviewItem; import org.fdroid.database.Repository; import org.fdroid.download.DownloadRequest; @@ -77,6 +78,7 @@ import java.net.Socket; import java.security.MessageDigest; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Formatter; @@ -475,27 +477,20 @@ public final class Utils { * @see Preferences#isBackgroundDownloadAllowed() */ public static void setIconFromRepoOrPM(@NonNull App app, ImageView iv, Context context) { - long repoId = app.repoId; - String iconPath = app.iconFromApk; - if (iconPath == null) { - Glide.with(context).clear(iv); - iv.setImageResource(R.drawable.ic_repo_app_default); - } else { - loadWithGlide(context, repoId, iconPath, iv); - } + loadWithGlide(context, app.repoId, app.iconFile, iv); } @Deprecated public static void setIconFromRepoOrPM(@NonNull AppOverviewItem app, ImageView iv, Context context) { long repoId = app.getRepoId(); - FileV2 iconFile = app.getIcon(App.getLocales()); - String iconPath = iconFile == null ? null : iconFile.getName(); - loadWithGlide(context, repoId, iconPath, iv); + IndexFile iconFile = app.getIcon(App.getLocales()); + loadWithGlide(context, repoId, iconFile, iv); } - private static void loadWithGlide(Context context, long repoId, String iconPath, ImageView iv) { - if (iconPath == null) { + public static void loadWithGlide(Context context, long repoId, @Nullable IndexFile file, ImageView iv) { + if (file == null) { Glide.with(context).clear(iv); + iv.setImageResource(R.drawable.ic_repo_app_default); return; } if (iconRequestOptions == null) { @@ -513,15 +508,24 @@ public final class Utils { } String address = getRepoAddress(repo); if (address.startsWith("content://")) { - String uri = getUri(address, iconPath.split("/")).toString(); + String uri = getUri(address, file.getName().split("/")).toString(); Glide.with(context).load(uri).apply(options).into(iv); } else { - DownloadRequest request = getDownloadRequest(repo, iconPath); + DownloadRequest request = getDownloadRequest(repo, file); Glide.with(context).load(request).apply(options).into(iv); } } @Nullable + public static DownloadRequest getDownloadRequest(@NonNull Repository repo, @Nullable IndexFile file) { + if (file == null) return null; + List mirrors = repo.getMirrors(); + Proxy proxy = NetCipher.getProxy(); + return new DownloadRequest(file, mirrors, proxy, repo.getUsername(), repo.getPassword()); + } + + @Nullable + @Deprecated public static DownloadRequest getDownloadRequest(@NonNull Repository repo, @Nullable String path) { if (path == null) return null; List mirrors = repo.getMirrors(); @@ -854,6 +858,23 @@ public final class Utils { }); } + public static ArrayList toString(@Nullable List files) { + if (files == null) return new ArrayList<>(0); + ArrayList list = new ArrayList<>(files.size()); + for (FileV2 file : files) { + list.add(file.serialize()); + } + return list; + } + + public static List fileV2FromStrings(List list) { + ArrayList files = new ArrayList<>(list.size()); + for (String s : list) { + files.add(FileV2.deserialize(s)); + } + return files; + } + /** * Keep an instance of this class as an field in an AppCompatActivity for figuring out whether the on * screen keyboard is currently visible or not. 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 653627ca9..741c7a4f3 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java @@ -19,6 +19,7 @@ import org.fdroid.fdroid.CompatibilityChecker; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.installer.ApkCache; +import org.fdroid.index.v2.FileV1; import org.fdroid.index.v2.PermissionV2; import org.fdroid.index.v2.SignerV2; @@ -60,7 +61,6 @@ public class Apk implements Comparable, Parcelable { // these are never set by the Apk/package index metadata public String repoAddress; public String canonicalRepoAddress; - long repoVersion; public SanitizedFile installedFile; // the .apk file on this device's filesystem public boolean compatible; // True if compatible with the device. public long repoId; // the database ID of the repo it comes from @@ -107,13 +107,20 @@ public class Apk implements Comparable, Parcelable { */ public String sig; - public String apkName; // F-Droid style APK name + /** + * Can be null when created with {@link #Apk(PackageInfo)} + * which happens only for showing an installed version + * in {@link org.fdroid.fdroid.views.AppDetailsActivity}. + */ + @Nullable + public FileV1 apkFile; /** * If not null, this is the name of the source tarball for the * application. Null indicates that it's a developer's binary * build - otherwise it's built from source. */ + @Nullable public String srcname; public String[] incompatibleReasons; @@ -122,11 +129,6 @@ public class Apk implements Comparable, Parcelable { public String whatsNew; - /** - * The numeric primary key of the Metadata table, which is used to join apks. - */ - public long appId; - public Apk() { } @@ -154,9 +156,6 @@ public class Apk implements Comparable, Parcelable { Repository repo = Objects.requireNonNull(FDroidApp.getRepo(v.getRepoId())); repoAddress = Utils.getRepoAddress(repo); canonicalRepoAddress = repo.getAddress(); - repoVersion = repo.getVersion(); - hash = v.getFile().getSha256(); - hashType = "sha256"; added = new Date(v.getAdded()); features = v.getFeatureNames().toArray(new String[0]); setPackageName(v.getPackageName()); @@ -174,11 +173,7 @@ public class Apk implements Comparable, Parcelable { } else { releaseChannels = channels; } - // obbMainFile = cursor.getString(i); - // obbMainFileSha256 = cursor.getString(i); - // obbPatchFile = cursor.getString(i); - // obbPatchFileSha256 = cursor.getString(i); - apkName = v.getFile().getName(); + apkFile = v.getFile(); setRequestedPermissions(v.getUsesPermission(), 0); setRequestedPermissions(v.getUsesPermissionSdk23(), 23); nativecode = v.getNativeCode().toArray(new String[0]); @@ -205,18 +200,23 @@ public class Apk implements Comparable, Parcelable { } private void checkRepoAddress() { - if (repoAddress == null || apkName == null) { + if (repoAddress == null || apkFile == 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 " + "[package: " + packageName + ", versionCode: " + versionCode - + ", apkName: " + apkName + + ", apkName: " + getApkPath() + ", repoAddress: " + repoAddress + ", repoId: " + repoId + "]"); } } + @Nullable + public String getApkPath() { + return apkFile == null ? "" : apkFile.getName(); + } + /** * Get the URL that points to the canonical download source for this * package. This is also used as the unique ID for tracking downloading, @@ -229,12 +229,12 @@ public class Apk implements Comparable, Parcelable { public String getCanonicalUrl() { checkRepoAddress(); /* Each String in pathElements might contain a /, should keep these as path elements */ - return Utils.getUri(canonicalRepoAddress, apkName.split("/")).toString(); + return Utils.getUri(canonicalRepoAddress, getApkPath().split("/")).toString(); } public String getDownloadUrl() { checkRepoAddress(); - return Utils.getUri(repoAddress, apkName.split("/")).toString(); + return Utils.getUri(repoAddress, getApkPath().split("/")).toString(); } /** @@ -311,8 +311,6 @@ public class Apk implements Comparable, Parcelable { dest.writeLong(this.versionCode); dest.writeLong(this.size); dest.writeLong(this.repoId); - dest.writeString(this.hash); - dest.writeString(this.hashType); dest.writeInt(this.minSdkVersion); dest.writeInt(this.targetSdkVersion); dest.writeInt(this.maxSdkVersion); @@ -326,15 +324,13 @@ public class Apk implements Comparable, Parcelable { dest.writeStringArray(this.nativecode); dest.writeString(this.sig); dest.writeByte(this.compatible ? (byte) 1 : (byte) 0); - dest.writeString(this.apkName); + dest.writeString(this.apkFile.serialize()); dest.writeSerializable(this.installedFile); dest.writeString(this.srcname); - dest.writeLong(this.repoVersion); dest.writeString(this.repoAddress); dest.writeString(this.canonicalRepoAddress); dest.writeStringArray(this.incompatibleReasons); dest.writeStringArray(this.antiFeatures); - dest.writeLong(this.appId); } protected Apk(Parcel in) { @@ -343,8 +339,6 @@ public class Apk implements Comparable, Parcelable { this.versionCode = in.readLong(); this.size = in.readLong(); this.repoId = in.readLong(); - this.hash = in.readString(); - this.hashType = in.readString(); this.minSdkVersion = in.readInt(); this.targetSdkVersion = in.readInt(); this.maxSdkVersion = in.readInt(); @@ -359,15 +353,13 @@ public class Apk implements Comparable, Parcelable { this.nativecode = in.createStringArray(); this.sig = in.readString(); this.compatible = in.readByte() != 0; - this.apkName = in.readString(); + this.apkFile = FileV1.deserialize(in.readString()); this.installedFile = (SanitizedFile) in.readSerializable(); this.srcname = in.readString(); - this.repoVersion = in.readLong(); this.repoAddress = in.readString(); this.canonicalRepoAddress = in.readString(); this.incompatibleReasons = in.createStringArray(); this.antiFeatures = in.createStringArray(); - this.appId = in.readLong(); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -536,7 +528,7 @@ public class Apk implements Comparable, Parcelable { } public File getInstalledMediaFile(Context context) { - return new File(this.getMediaInstallPath(context), SanitizedFile.sanitizeFileName(this.apkName)); + return new File(this.getMediaInstallPath(context), SanitizedFile.sanitizeFileName(getApkPath())); } /** @@ -554,7 +546,8 @@ public class Apk implements Comparable, Parcelable { * @return true if this is an apk instead of a non-apk/media file */ public boolean isApk() { - return apkName == null - || apkName.substring(apkName.length() - 4).toLowerCase(Locale.ENGLISH).endsWith(".apk"); + return apkFile == null + || apkFile.getName().substring(apkFile.getName().length() - 4) + .toLowerCase(Locale.ENGLISH).endsWith(".apk"); } } 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 35e3d1de8..4d1bf39eb 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -19,6 +19,7 @@ import android.util.Log; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestBuilder; +import org.fdroid.IndexFile; import org.fdroid.database.AppListItem; import org.fdroid.database.Repository; import org.fdroid.database.UpdatableApp; @@ -86,16 +87,10 @@ public class App implements Comparable, Parcelable { public String installedSig; public int installedVersionCode; public String installedVersionName; - private long id; public org.fdroid.database.AppPrefs prefs; public String preferredSigner; public boolean isApk; - /** - * Has this {@code App} been localized into one of the user's current locales. - */ - boolean isLocalized; - /** * This is primarily for the purpose of saving app metadata when parsing an index.xml file. * At most other times, we don't particularly care which repo an {@link App} object came from. @@ -110,7 +105,6 @@ public class App implements Comparable, Parcelable { public String name = "Unknown"; public String summary = "Unknown application"; - public String iconFromApk; public String description; @@ -119,15 +113,15 @@ public class App implements Comparable, Parcelable { */ public String whatsNew; - public String featureGraphic; - public String promoGraphic; - public String tvBanner; + public FileV2 featureGraphic; + private FileV2 promoGraphic; + private FileV2 tvBanner; - public String[] phoneScreenshots = new String[0]; - public String[] sevenInchScreenshots = new String[0]; - public String[] tenInchScreenshots = new String[0]; - public String[] tvScreenshots = new String[0]; - public String[] wearScreenshots = new String[0]; + public List phoneScreenshots = Collections.emptyList(); + private List sevenInchScreenshots = Collections.emptyList(); + private List tenInchScreenshots = Collections.emptyList(); + private List tvScreenshots = Collections.emptyList(); + private List wearScreenshots = Collections.emptyList(); public String license; @@ -205,18 +199,7 @@ public class App implements Comparable, Parcelable { @Nullable public String[] antiFeatures; - /** - * Requires root access (only ever used for root) - */ - @Nullable - @Deprecated - public String[] requirements; - - /** - * URL to download the app's icon. (Set only from localized block, see also - * {@link #iconFromApk} and {@link #getIconPath(Context)} (Context)} - */ - public String iconUrl; + public FileV2 iconFile; @Override public int compareTo(@NonNull App app) { @@ -227,20 +210,16 @@ public class App implements Comparable, Parcelable { } public App(final UpdatableApp app) { - id = 0; repoId = app.getUpdate().getRepoId(); setPackageName(app.getPackageName()); name = app.getName() == null ? "" : app.getName(); summary = app.getSummary() == null ? "" : app.getSummary(); installedVersionCode = (int) app.getInstalledVersionCode(); autoInstallVersionCode = (int) app.getUpdate().getManifest().getVersionCode(); - FileV2 icon = app.getIcon(getLocales()); - iconUrl = icon == null ? null : icon.getName(); - iconFromApk = icon == null ? null : icon.getName(); + iconFile = app.getIcon(getLocales()); } public App(final org.fdroid.database.App app, @Nullable PackageInfo packageInfo) { - id = 0; repoId = app.getRepoId(); compatible = app.getMetadata().isCompatible(); setPackageName(app.getPackageName()); @@ -269,40 +248,15 @@ public class App implements Comparable, Parcelable { preferredSigner = app.getMetadata().getPreferredSigner(); added = new Date(app.getMetadata().getAdded()); lastUpdated = new Date(app.getMetadata().getLastUpdated()); - FileV2 icon = app.getIcon(getLocales()); - iconUrl = icon == null ? null : icon.getName(); - iconFromApk = icon == null ? null : icon.getName(); - FileV2 featureGraphic = app.getFeatureGraphic(getLocales()); - this.featureGraphic = featureGraphic == null ? null : featureGraphic.getName(); - FileV2 promoGraphic = app.getPromoGraphic(getLocales()); - this.promoGraphic = promoGraphic == null ? null : promoGraphic.getName(); - FileV2 tvBanner = app.getPromoGraphic(getLocales()); - this.tvBanner = tvBanner == null ? null : tvBanner.getName(); - List phoneFiles = app.getPhoneScreenshots(getLocales()); - phoneScreenshots = new String[phoneFiles.size()]; - for (int i = 0; i < phoneFiles.size(); i++) { - phoneScreenshots[i] = phoneFiles.get(i).getName(); - } - List sevenInchFiles = app.getSevenInchScreenshots(getLocales()); - sevenInchScreenshots = new String[sevenInchFiles.size()]; - for (int i = 0; i < sevenInchFiles.size(); i++) { - phoneScreenshots[i] = sevenInchFiles.get(i).getName(); - } - List tenInchFiles = app.getTenInchScreenshots(getLocales()); - tenInchScreenshots = new String[tenInchFiles.size()]; - for (int i = 0; i < tenInchFiles.size(); i++) { - phoneScreenshots[i] = tenInchFiles.get(i).getName(); - } - List tvFiles = app.getTvScreenshots(getLocales()); - tvScreenshots = new String[tvFiles.size()]; - for (int i = 0; i < tvFiles.size(); i++) { - phoneScreenshots[i] = tvFiles.get(i).getName(); - } - List wearFiles = app.getWearScreenshots(getLocales()); - wearScreenshots = new String[wearFiles.size()]; - for (int i = 0; i < wearFiles.size(); i++) { - phoneScreenshots[i] = wearFiles.get(i).getName(); - } + iconFile = app.getIcon(getLocales()); + featureGraphic = app.getFeatureGraphic(getLocales()); + promoGraphic = app.getPromoGraphic(getLocales()); + tvBanner = app.getPromoGraphic(getLocales()); + phoneScreenshots = app.getPhoneScreenshots(getLocales()); + sevenInchScreenshots = app.getSevenInchScreenshots(getLocales()); + tenInchScreenshots = app.getTenInchScreenshots(getLocales()); + tvScreenshots = app.getTvScreenshots(getLocales()); + wearScreenshots = app.getWearScreenshots(getLocales()); setInstalled(packageInfo); } @@ -311,8 +265,7 @@ public class App implements Comparable, Parcelable { setPackageName(item.getPackageName()); name = item.getName() == null ? "" : item.getName(); summary = item.getSummary() == null ? "" : item.getSummary(); - FileV2 iconFile = item.getIcon(getLocales()); - iconFromApk = iconFile == null ? null : iconFile.getName(); + iconFile = item.getIcon(getLocales()); installedVersionCode = item.getInstalledVersionCode() == null ? 0 : item.getInstalledVersionCode().intValue(); installedVersionName = item.getInstalledVersionName(); antiFeatures = item.getAntiFeatureKeys().toArray(new String[0]); @@ -367,7 +320,7 @@ public class App implements Comparable, Parcelable { /** * Set the Package Name property while ensuring it is sanitized. */ - void setPackageName(String packageName) { + private void setPackageName(String packageName) { if (Utils.isSafePackageName(packageName)) { this.packageName = packageName; } else { @@ -379,7 +332,7 @@ public class App implements Comparable, Parcelable { /** * Returns the app description text with all newlines replaced by {@code
} */ - public static String formatDescription(String description) { + private static String formatDescription(String description) { return description.replace("\n", "
"); } @@ -395,11 +348,11 @@ public class App implements Comparable, Parcelable { .build(); } - public RequestBuilder loadWithGlide(Context context, String path) { - return loadWithGlide(context, repoId, path); + public RequestBuilder loadWithGlide(Context context, IndexFile file) { + return loadWithGlide(context, repoId, file); } - public static RequestBuilder loadWithGlide(Context context, long repoId, String path) { + public static RequestBuilder loadWithGlide(Context context, long repoId, IndexFile file) { Repository repo = FDroidApp.getRepo(repoId); if (repo == null) { // This is also used for apps that do not have a repo return Glide.with(context).load((Drawable) null); @@ -410,12 +363,12 @@ public class App implements Comparable, Parcelable { } String address = Utils.getRepoAddress(repo); if (address.startsWith("content://")) { - String sb = Utils.getUri(address, path.split("/")).toString(); + String sb = Utils.getUri(address, file.getName().split("/")).toString(); return Glide.with(context).load(sb); } else if (address.startsWith("file://")) { - return Glide.with(context).load(path); + return Glide.with(context).load(file); } else { - return Glide.with(context).load(Utils.getDownloadRequest(repo, path)); + return Glide.with(context).load(Utils.getDownloadRequest(repo, file)); } } @@ -438,23 +391,18 @@ public class App implements Comparable, Parcelable { } } - public String getIconPath(Context context) { - String path; - if (TextUtils.isEmpty(iconUrl)) { - if (TextUtils.isEmpty(iconFromApk)) { - return null; - } - if (iconFromApk.endsWith(".xml")) { - // We cannot use xml resources as icons. F-Droid server should not include them - // https://gitlab.com/fdroid/fdroidserver/issues/344 - return null; - } - String iconsDir = Utils.getIconsDir(context, 1.0); - path = getPath(iconsDir, iconFromApk); + public String getIconPath() { + if (iconFile == null) { + return null; + } else if (TextUtils.isEmpty(iconFile.getName())) { + return null; + } else if (iconFile.getName().endsWith(".xml")) { + // We cannot use xml resources as icons. F-Droid server should not include them + // https://gitlab.com/fdroid/fdroidserver/issues/344 + return null; } else { - path = iconUrl; + return iconFile.getName(); } - return path; } /** @@ -477,23 +425,12 @@ public class App implements Comparable, Parcelable { return sb.toString(); } - public ArrayList getAllScreenshots() { - ArrayList list = new ArrayList<>(); - if (phoneScreenshots != null) { - Collections.addAll(list, phoneScreenshots); - } - if (sevenInchScreenshots != null) { - Collections.addAll(list, sevenInchScreenshots); - } - if (tenInchScreenshots != null) { - Collections.addAll(list, tenInchScreenshots); - } - if (tvScreenshots != null) { - Collections.addAll(list, tvScreenshots); - } - if (wearScreenshots != null) { - Collections.addAll(list, wearScreenshots); - } + public List getAllScreenshots() { + ArrayList list = new ArrayList<>(phoneScreenshots); + list.addAll(sevenInchScreenshots); + list.addAll(tenInchScreenshots); + list.addAll(tvScreenshots); + list.addAll(wearScreenshots); return list; } @@ -737,10 +674,6 @@ public class App implements Comparable, Parcelable { return new int[]{minSdkVersion, targetSdkVersion, maxSdkVersion}; } - public long getId() { - return id; - } - @Override public int describeContents() { return 0; @@ -774,7 +707,7 @@ public class App implements Comparable, Parcelable { dest.writeString(this.name); dest.writeLong(this.repoId); dest.writeString(this.summary); - dest.writeString(this.iconFromApk); + dest.writeString(this.iconFile == null ? null : this.iconFile.serialize()); dest.writeString(this.description); dest.writeString(this.whatsNew); dest.writeString(this.license); @@ -801,23 +734,19 @@ public class App implements Comparable, Parcelable { dest.writeLong(this.lastUpdated != null ? this.lastUpdated.getTime() : -1); dest.writeStringArray(this.categories); dest.writeStringArray(this.antiFeatures); - dest.writeStringArray(this.requirements); - dest.writeString(this.iconUrl); - dest.writeString(this.featureGraphic); - dest.writeString(this.promoGraphic); - dest.writeString(this.tvBanner); - dest.writeStringArray(this.phoneScreenshots); - dest.writeStringArray(this.sevenInchScreenshots); - dest.writeStringArray(this.tenInchScreenshots); - dest.writeStringArray(this.tvScreenshots); - dest.writeStringArray(this.wearScreenshots); + dest.writeString(this.featureGraphic == null ? null : this.featureGraphic.serialize()); + dest.writeString(this.promoGraphic == null ? null : this.promoGraphic.serialize()); + dest.writeString(this.tvBanner == null ? null : this.tvBanner.serialize()); + dest.writeStringList(Utils.toString(this.phoneScreenshots)); + dest.writeStringList(Utils.toString(this.sevenInchScreenshots)); + dest.writeStringList(Utils.toString(this.tenInchScreenshots)); + dest.writeStringList(Utils.toString(this.tvScreenshots)); + dest.writeStringList(Utils.toString(this.wearScreenshots)); dest.writeByte(this.isApk ? (byte) 1 : (byte) 0); - dest.writeByte(this.isLocalized ? (byte) 1 : (byte) 0); dest.writeString(this.installedVersionName); dest.writeInt(this.installedVersionCode); dest.writeParcelable(this.installedApk, flags); dest.writeString(this.installedSig); - dest.writeLong(this.id); } protected App(Parcel in) { @@ -826,7 +755,7 @@ public class App implements Comparable, Parcelable { this.name = in.readString(); this.repoId = in.readLong(); this.summary = in.readString(); - this.iconFromApk = in.readString(); + this.iconFile = FileV2.deserialize(in.readString()); this.description = in.readString(); this.whatsNew = in.readString(); this.license = in.readString(); @@ -855,23 +784,19 @@ public class App implements Comparable, Parcelable { this.lastUpdated = tmpLastUpdated == -1 ? null : new Date(tmpLastUpdated); this.categories = in.createStringArray(); this.antiFeatures = in.createStringArray(); - this.requirements = in.createStringArray(); - this.iconUrl = in.readString(); - this.featureGraphic = in.readString(); - this.promoGraphic = in.readString(); - this.tvBanner = in.readString(); - this.phoneScreenshots = in.createStringArray(); - this.sevenInchScreenshots = in.createStringArray(); - this.tenInchScreenshots = in.createStringArray(); - this.tvScreenshots = in.createStringArray(); - this.wearScreenshots = in.createStringArray(); + this.featureGraphic = FileV2.deserialize(in.readString()); + this.promoGraphic = FileV2.deserialize(in.readString()); + this.tvBanner = FileV2.deserialize(in.readString()); + this.phoneScreenshots = Utils.fileV2FromStrings(in.createStringArrayList()); + this.sevenInchScreenshots = Utils.fileV2FromStrings(in.createStringArrayList()); + this.tenInchScreenshots = Utils.fileV2FromStrings(in.createStringArrayList()); + this.tvScreenshots = Utils.fileV2FromStrings(in.createStringArrayList()); + this.wearScreenshots = Utils.fileV2FromStrings(in.createStringArrayList()); this.isApk = in.readByte() != 0; - this.isLocalized = in.readByte() != 0; this.installedVersionName = in.readString(); this.installedVersionCode = in.readInt(); this.installedApk = in.readParcelable(Apk.class.getClassLoader()); this.installedSig = in.readString(); - this.id = in.readLong(); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { diff --git a/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java b/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java index ae4218a5f..fd4e0bc78 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java @@ -59,7 +59,7 @@ public class ApkCache { throws IOException { String name = expectedApk.packageName; String apkFileName = name + "-" + expectedApk.versionName + ".apk"; - return copyApkToFiles(context, apkFile, apkFileName, true, expectedApk.hash, expectedApk.hashType); + return copyApkToFiles(context, apkFile, apkFileName, true, expectedApk.apkFile.getSha256(), "sha256"); } /** @@ -133,7 +133,7 @@ public class ApkCache { */ public static boolean apkIsCached(File apkFile, Apk apkToCheck) { return apkFile.length() == apkToCheck.size && - Utils.isFileMatchingHash(apkFile, apkToCheck.hash, apkToCheck.hashType); + Utils.isFileMatchingHash(apkFile, apkToCheck.apkFile.getSha256(), "sha256"); } /** diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java index 638dc0f72..36d58c9f9 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java @@ -15,7 +15,6 @@ import android.util.Log; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.WildcardFileFilter; -import org.fdroid.download.Downloader; import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.compat.PackageManagerCompat; @@ -69,7 +68,7 @@ import static vendored.org.apache.commons.codec.digest.MessageDigestAlgorithms.S *
  • for a {@code String} ID, use {@code canonicalUrl}, {@link Uri#toString()}, or * {@link Intent#getDataString()} *
  • for an {@code int} ID, use {@link String#hashCode()} or {@link Uri#hashCode()} - *
  • for an {@link Intent} extra, use {@link Downloader#EXTRA_CANONICAL_URL} and include a + *
  • for an {@link Intent} extra, use {@link DownloaderService#EXTRA_CANONICAL_URL} and include a * {@link String} instance *

    * The implementations of {@link Uri#toString()} and {@link Intent#getDataString()} both @@ -214,7 +213,7 @@ public class InstallManagerService extends Service { long apkFileSize = apkFilePath.length(); if (!apkFilePath.exists() || apkFileSize < apk.size) { Utils.debugLog(TAG, "download " + canonicalUrl + " " + apkFilePath); - DownloaderService.queue(this, apk.repoId, canonicalUrl, apk.getDownloadUrl()); + DownloaderService.queue(this, apk.repoId, canonicalUrl, apk.getDownloadUrl(), apk.apkFile); } else if (ApkCache.apkIsCached(apkFilePath, apk)) { Utils.debugLog(TAG, "skip download, we have it, straight to install " + canonicalUrl + " " + apkFilePath); sendBroadcast(intent.getData(), DownloaderService.ACTION_STARTED, apkFilePath); @@ -222,7 +221,7 @@ public class InstallManagerService extends Service { } else { Utils.debugLog(TAG, "delete and download again " + canonicalUrl + " " + apkFilePath); apkFilePath.delete(); - DownloaderService.queue(this, apk.repoId, canonicalUrl, apk.getDownloadUrl()); + DownloaderService.queue(this, apk.repoId, canonicalUrl, apk.getDownloadUrl(), apk.apkFile); } return START_REDELIVER_INTENT; // if killed before completion, retry Intent @@ -307,7 +306,7 @@ public class InstallManagerService extends Service { } } }; - DownloaderService.queue(this, repoId, obbUrlString, obbUrlString); + DownloaderService.queue(this, repoId, obbUrlString, obbUrlString, null); localBroadcastManager.registerReceiver(downloadReceiver, DownloaderService.getIntentFilter(obbUrlString)); } diff --git a/app/src/main/java/org/fdroid/fdroid/net/BluetoothDownloader.java b/app/src/main/java/org/fdroid/fdroid/net/BluetoothDownloader.java index 511dadc75..4b4ca5bfc 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/BluetoothDownloader.java +++ b/app/src/main/java/org/fdroid/fdroid/net/BluetoothDownloader.java @@ -4,6 +4,7 @@ import android.net.Uri; import android.util.Log; import org.apache.commons.io.input.BoundedInputStream; +import org.fdroid.IndexFile; import org.fdroid.download.Downloader; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.nearby.BluetoothClient; @@ -39,8 +40,8 @@ public class BluetoothDownloader extends Downloader { && Pattern.matches("([0-9A-F]{2}-)+[0-9A-F]{2}", uri.getHost()); } - public BluetoothDownloader(Uri uri, File destFile) throws IOException { - super(destFile); + BluetoothDownloader(Uri uri, IndexFile indexFile, File destFile) throws IOException { + super(indexFile, destFile); String macAddress = uri.getHost().replace("-", ":"); this.connection = new BluetoothClient(macAddress).openConnection(); this.sourcePath = uri.getPath(); @@ -91,18 +92,11 @@ public class BluetoothDownloader extends Downloader { @Override public long totalDownloadSize() { - if (getFileSize() != null) return getFileSize(); + if (getIndexFile().getSize() != null) return getIndexFile().getSize(); FileDetails details = getFileDetails(); return details != null ? details.getFileSize() : -1; } - @Override - public void download(long totalSize, @Nullable String sha256) throws IOException, InterruptedException { - setFileSize(totalSize); - setSha256(sha256); - download(); - } - @Override public void download() throws IOException, InterruptedException { downloadFromStream(false); diff --git a/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java b/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java index e94b5a8b4..4fdd79307 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java +++ b/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java @@ -6,18 +6,23 @@ import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.fdroid.IndexFile; import org.fdroid.database.Repository; import org.fdroid.download.DownloadRequest; import org.fdroid.download.Downloader; import org.fdroid.download.HttpDownloader; +import org.fdroid.download.HttpDownloaderV2; import org.fdroid.download.HttpManager; import org.fdroid.download.Mirror; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Utils; +import org.fdroid.index.IndexFormatVersion; import java.io.File; import java.io.IOException; import java.net.Proxy; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import info.guardianproject.netcipher.NetCipher; @@ -32,33 +37,60 @@ public class DownloaderFactory extends org.fdroid.download.DownloaderFactory { @NonNull @Override - public Downloader create(Repository repo, @NonNull Uri uri, @NonNull File destFile) throws IOException { + public Downloader create(Repository repo, @NonNull Uri uri, @NonNull IndexFile indexFile, + @NonNull File destFile) throws IOException { List mirrors = repo.getMirrors(); - return create(repo, mirrors, uri, destFile, null); + return create(repo, mirrors, uri, indexFile, destFile, null); } @NonNull @Override protected Downloader create(@NonNull Repository repo, @NonNull List mirrors, @NonNull Uri uri, - @NonNull File destFile, @Nullable Mirror tryFirst) throws IOException { + @NonNull IndexFile indexFile, @NonNull File destFile, + @Nullable Mirror tryFirst) throws IOException { Downloader downloader; String scheme = uri.getScheme(); if (BluetoothDownloader.SCHEME.equals(scheme)) { - downloader = new BluetoothDownloader(uri, destFile); + downloader = new BluetoothDownloader(uri, indexFile, destFile); } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) { - downloader = new TreeUriDownloader(uri, destFile); + downloader = new TreeUriDownloader(uri, indexFile, destFile); } else if (ContentResolver.SCHEME_FILE.equals(scheme)) { - downloader = new LocalFileDownloader(uri, destFile); + downloader = new LocalFileDownloader(uri, indexFile, destFile); } else { String repoAddress = Utils.getRepoAddress(repo); String path = uri.toString().replace(repoAddress, ""); Utils.debugLog(TAG, "Using suffix " + path + " with mirrors " + mirrors); Proxy proxy = NetCipher.getProxy(); - DownloadRequest request = new DownloadRequest(path, mirrors, proxy, repo.getUsername(), - repo.getPassword(), tryFirst); - downloader = new HttpDownloader(HTTP_MANAGER, request, destFile); + DownloadRequest request = new DownloadRequest(indexFile, mirrors, proxy, + repo.getUsername(), repo.getPassword(), tryFirst); + if (repo.getFormatVersion() == null || repo.getFormatVersion() == IndexFormatVersion.ONE) { + //noinspection deprecation + downloader = new HttpDownloader(HTTP_MANAGER, request, destFile); + } else { + DownloadRequest r; + if (request.getIndexFile().getIpfsCidV1() == null) r = request; + else { + // add IPFS gateways to mirrors, because have have a CIDv1 + List m = new ArrayList<>(mirrors); + m.addAll(IPFS_MIRRORS); + r = new DownloadRequest(request.getIndexFile(), m, proxy, repo.getUsername(), + repo.getPassword(), tryFirst); + } + downloader = new HttpDownloaderV2(HTTP_MANAGER, r, destFile); + } } return downloader; } + + private static final List IPFS_MIRRORS = Arrays.asList( + new Mirror("https://ipfs.eth.aragon.network/ipfs/", null, true), + new Mirror("https://gateway.ipfs.io/ipfs/", null, true), + new Mirror("https://ipfs.io/ipfs/", null, true), + new Mirror("https://cloudflare-ipfs.com/ipfs/", null, true), + new Mirror("https://ipfs.fleek.co/ipfs/", null, true), + new Mirror("https://gateway.pinata.cloud/ipfs/", null, true), + new Mirror("https://ipfs.filebase.io/ipfs/", null, true) + ); + } diff --git a/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java b/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java index 6bc009b86..bef7b389d 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java +++ b/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java @@ -43,6 +43,7 @@ import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.DBHelper; import org.fdroid.fdroid.data.SanitizedFile; import org.fdroid.fdroid.installer.ApkCache; +import org.fdroid.index.v2.FileV1; import java.io.File; import java.io.IOException; @@ -63,11 +64,11 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; /** * DownloaderService is a service that handles asynchronous download requests * (expressed as {@link Intent}s) on demand. Clients send download requests - * through {@link #queue(Context, long, String)} calls. The + * through {@link #queue(Context, long, String, String, FileV1)} calls. The * service is started as needed, it handles each {@code Intent} using a worker * thread, and stops itself when it runs out of work. Requests can be canceled * using {@link #cancel(Context, String)}. If this service is killed during - * operation, it will receive the queued {@link #queue(Context, long, String)} + * operation, it will receive the queued {@link #queue(Context, long, String, String, FileV1)} * and {@link #cancel(Context, String)} requests again due to * {@link Service#START_REDELIVER_INTENT}. Bad requests will be ignored, * including on restart after killing via {@link Service#START_NOT_STICKY}. @@ -123,6 +124,7 @@ public class DownloaderService extends Service { * @see android.content.Intent#EXTRA_ORIGINATING_URI */ public static final String EXTRA_CANONICAL_URL = "org.fdroid.fdroid.net.Downloader.extra.CANONICAL_URL"; + private static final String EXTRA_INDEX_FILE_V1 = "org.fdroid.fdroid.net.Downloader.extra.INDEX_FILE_V1"; private volatile Looper serviceLooper; private static volatile ServiceHandler serviceHandler; @@ -246,6 +248,7 @@ public class DownloaderService extends Service { final Uri canonicalUrl = intent.getData(); final Uri downloadUrl = Uri.parse(intent.getStringExtra(DownloaderService.EXTRA_CANONICAL_URL)); + final FileV1 fileV1 = FileV1.deserialize(intent.getStringExtra(DownloaderService.EXTRA_INDEX_FILE_V1)); final SanitizedFile localFile = ApkCache.getApkDownloadPath(this, canonicalUrl); sendBroadcast(uri, DownloaderService.ACTION_STARTED, localFile, repoId, canonicalUrl); @@ -264,7 +267,7 @@ public class DownloaderService extends Service { } else return; // repo might have been deleted in the meantime } } - downloader = DownloaderFactory.INSTANCE.create(repo, downloadUrl, localFile); + downloader = DownloaderFactory.INSTANCE.create(repo, downloadUrl, fileV1, localFile); downloader.setListener(new ProgressListener() { @Override public void onProgress(long bytesRead, long totalBytes) { @@ -334,7 +337,7 @@ public class DownloaderService extends Service { * @see #cancel(Context, String) */ public static void queue(Context context, long repoId, String canonicalUrl, - String downloadUrl) { + String downloadUrl, FileV1 fileV1) { if (TextUtils.isEmpty(canonicalUrl)) { return; } @@ -344,6 +347,7 @@ public class DownloaderService extends Service { intent.setData(Uri.parse(canonicalUrl)); intent.putExtra(DownloaderService.EXTRA_REPO_ID, repoId); intent.putExtra(DownloaderService.EXTRA_CANONICAL_URL, downloadUrl); + intent.putExtra(DownloaderService.EXTRA_INDEX_FILE_V1, fileV1.serialize()); context.startService(intent); } @@ -354,7 +358,7 @@ public class DownloaderService extends Service { * * @param context this app's {@link Context} * @param canonicalUrl The URL to remove from the download queue - * @see #queue(Context, long, String) + * @see #queue(Context, long, String, String, FileV1 */ public static void cancel(Context context, String canonicalUrl) { if (TextUtils.isEmpty(canonicalUrl)) { diff --git a/app/src/main/java/org/fdroid/fdroid/net/LocalFileDownloader.java b/app/src/main/java/org/fdroid/fdroid/net/LocalFileDownloader.java index 72cae0aff..3dd522f9c 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/LocalFileDownloader.java +++ b/app/src/main/java/org/fdroid/fdroid/net/LocalFileDownloader.java @@ -3,10 +3,10 @@ package org.fdroid.fdroid.net; import android.net.Uri; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.fdroid.IndexFile; import org.fdroid.download.Downloader; import org.fdroid.download.NotFoundException; @@ -31,8 +31,8 @@ public class LocalFileDownloader extends Downloader { private InputStream inputStream; private final File sourceFile; - LocalFileDownloader(Uri uri, File destFile) { - super(destFile); + LocalFileDownloader(Uri uri, IndexFile indexFile, File destFile) { + super(indexFile, destFile); sourceFile = new File(uri.getPath()); } @@ -73,13 +73,6 @@ public class LocalFileDownloader extends Downloader { return sourceFile.length(); } - @Override - public void download(long totalSize, @Nullable String sha256) throws IOException, InterruptedException { - setFileSize(totalSize); - setSha256(sha256); - download(); - } - @Override public void download() throws IOException, InterruptedException { if (!sourceFile.exists()) { diff --git a/app/src/main/java/org/fdroid/fdroid/net/TreeUriDownloader.java b/app/src/main/java/org/fdroid/fdroid/net/TreeUriDownloader.java index 2788ee594..830a7962f 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/TreeUriDownloader.java +++ b/app/src/main/java/org/fdroid/fdroid/net/TreeUriDownloader.java @@ -1,9 +1,9 @@ package org.fdroid.fdroid.net; -import android.annotation.TargetApi; import android.content.Context; import android.net.Uri; +import org.fdroid.IndexFile; import org.fdroid.download.Downloader; import org.fdroid.download.NotFoundException; import org.fdroid.fdroid.FDroidApp; @@ -16,7 +16,6 @@ import java.io.InputStream; import java.net.ProtocolException; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.documentfile.provider.DocumentFile; /** @@ -34,7 +33,6 @@ import androidx.documentfile.provider.DocumentFile; * @see Open Files using Storage Access Framework * @see Using Scoped Directory Access */ -@TargetApi(21) public class TreeUriDownloader extends Downloader { public static final String TAG = "TreeUriDownloader"; @@ -49,8 +47,8 @@ public class TreeUriDownloader extends Downloader { private final Uri treeUri; private final DocumentFile documentFile; - TreeUriDownloader(Uri uri, File destFile) { - super(destFile); + TreeUriDownloader(Uri uri, IndexFile indexFile, File destFile) { + super(indexFile, destFile); context = FDroidApp.getInstance(); String path = uri.getEncodedPath(); int lastEscapedSlash = path.lastIndexOf(ESCAPED_SLASH); @@ -102,14 +100,7 @@ public class TreeUriDownloader extends Downloader { @Override protected long totalDownloadSize() { - return getFileSize() != null ? getFileSize() : documentFile.length(); - } - - @Override - public void download(long totalSize, @Nullable String sha256) throws IOException, InterruptedException { - setFileSize(totalSize); - setSha256(sha256); - downloadFromStream(false); + return getIndexFile().getSize() != null ? getIndexFile().getSize() : documentFile.length(); } @Override diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java index 1cdb5328f..0f17adbb8 100644 --- a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java @@ -191,7 +191,7 @@ public class InstallConfirmActivity extends AppCompatActivity implements OnCance TextView appName = (TextView) appSnippet.findViewById(R.id.app_name); appName.setText(app.name); ImageView appIcon = (ImageView) appSnippet.findViewById(R.id.app_icon); - app.loadWithGlide(this, app.getIconPath(this)) + app.loadWithGlide(this, app.iconFile) .apply(Utils.getAlwaysShowIconRequestOptions()) .into(appIcon); } diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java index aa2ae0afc..e2f45dbc3 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java @@ -63,6 +63,7 @@ import org.fdroid.fdroid.privileged.views.AppDiff; import org.fdroid.fdroid.privileged.views.AppSecurityPermissions; import org.fdroid.fdroid.views.appdetails.AntiFeaturesListingView; import org.fdroid.fdroid.views.main.MainActivity; +import org.fdroid.index.v2.FileV2; import java.io.File; import java.util.ArrayList; @@ -148,8 +149,8 @@ public class AppDetailsRecyclerViewAdapter compatibleVersionsDifferentSig.add(apk); if (allowBySig) { versions.add(apk); - if (!versionsExpandTracker.containsKey(apk.apkName)) { - versionsExpandTracker.put(apk.apkName, false); + if (!versionsExpandTracker.containsKey(apk.getApkPath())) { + versionsExpandTracker.put(apk.getApkPath(), false); } } } @@ -713,7 +714,7 @@ public class AppDetailsRecyclerViewAdapter @Override public void onScreenshotClick(int position) { - ArrayList screenshots = Objects.requireNonNull(app).getAllScreenshots(); + List screenshots = Objects.requireNonNull(app).getAllScreenshots(); context.startActivity(ScreenShotsActivity.getStartIntent(context, app.repoId, screenshots, position)); } @@ -1104,7 +1105,8 @@ public class AppDetailsRecyclerViewAdapter TextUtils.equals(apk.sig, app.installedSig); boolean isApkSuggested = apk.equals(suggestedApk); boolean isApkDownloading = callbacks.isAppDownloading() && downloadedApk != null && - downloadedApk.compareTo(apk) == 0 && TextUtils.equals(apk.apkName, downloadedApk.apkName); + downloadedApk.compareTo(apk) == 0 && + TextUtils.equals(apk.getApkPath(), downloadedApk.getApkPath()); boolean isApkInstalledDummy = apk.versionCode == app.installedVersionCode && apk.compatible && apk.size == 0 && apk.maxSdkVersion == -1; @@ -1198,7 +1200,7 @@ public class AppDetailsRecyclerViewAdapter } // Expand the view if it was previously expanded or when downloading - expand(versionsExpandTracker.get(apk.apkName) || isApkDownloading); + expand(versionsExpandTracker.get(apk.getApkPath()) || isApkDownloading); // Toggle expanded view when clicking the whole version item, // unless it's an installed app version dummy item - it doesn't @@ -1293,7 +1295,7 @@ public class AppDetailsRecyclerViewAdapter } private void expand(boolean expand) { - versionsExpandTracker.put(apk.apkName, expand); + versionsExpandTracker.put(apk.getApkPath(), expand); expandedLayout.setVisibility(expand ? View.VISIBLE : View.GONE); versionCode.setVisibility(expand ? View.VISIBLE : View.GONE); expandArrow.setImageDrawable(ContextCompat.getDrawable(context, expand ? @@ -1314,7 +1316,7 @@ public class AppDetailsRecyclerViewAdapter return; } - boolean expand = !versionsExpandTracker.get(apk.apkName); + boolean expand = !versionsExpandTracker.get(apk.getApkPath()); expand(expand); if (expand) { diff --git a/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java b/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java index c0e24cb59..13ea5f72a 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java @@ -20,9 +20,10 @@ import androidx.viewpager.widget.ViewPager; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; +import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.App; +import org.fdroid.index.v2.FileV2; -import java.util.ArrayList; import java.util.List; /** @@ -41,11 +42,11 @@ public class ScreenShotsActivity extends AppCompatActivity { private static boolean allowDownload = true; - public static Intent getStartIntent(Context context, long repoId, ArrayList screenshots, + public static Intent getStartIntent(Context context, long repoId, List screenshots, int startPosition) { Intent intent = new Intent(context, ScreenShotsActivity.class); intent.putExtra(EXTRA_REPO_ID, repoId); - intent.putStringArrayListExtra(EXTRA_SCREENSHOT_LIST, screenshots); + intent.putStringArrayListExtra(EXTRA_SCREENSHOT_LIST, Utils.toString(screenshots)); intent.putExtra(EXTRA_START_POSITION, startPosition); return intent; } @@ -59,7 +60,8 @@ public class ScreenShotsActivity extends AppCompatActivity { setContentView(R.layout.activity_screenshots); long repoId = getIntent().getLongExtra(EXTRA_REPO_ID, 1); - List screenshots = getIntent().getStringArrayListExtra(EXTRA_SCREENSHOT_LIST); + List list = getIntent().getStringArrayListExtra(EXTRA_SCREENSHOT_LIST); + List screenshots = Utils.fileV2FromStrings(list); int startPosition = getIntent().getIntExtra(EXTRA_START_POSITION, 0); ViewPager viewPager = (ViewPager) findViewById(R.id.screenshot_view_pager); @@ -86,9 +88,9 @@ public class ScreenShotsActivity extends AppCompatActivity { private static class ScreenShotPagerAdapter extends FragmentStatePagerAdapter { private final long repoId; - private final List screenshots; + private final List screenshots; - ScreenShotPagerAdapter(FragmentManager fragmentManager, long repoId, List screenshots) { + ScreenShotPagerAdapter(FragmentManager fragmentManager, long repoId, List screenshots) { super(fragmentManager); this.repoId = repoId; this.screenshots = screenshots; @@ -113,23 +115,23 @@ public class ScreenShotsActivity extends AppCompatActivity { private static final String ARG_REPO_ID = "ARG_REPO_ID"; private static final String ARG_SCREENSHOT_URL = "ARG_SCREENSHOT_URL"; - static ScreenShotPageFragment newInstance(long repoId, @NonNull String screenshotUrl) { + static ScreenShotPageFragment newInstance(long repoId, @NonNull FileV2 screenshotUrl) { ScreenShotPageFragment fragment = new ScreenShotPageFragment(); Bundle args = new Bundle(); args.putLong(ARG_REPO_ID, repoId); - args.putString(ARG_SCREENSHOT_URL, screenshotUrl); + args.putString(ARG_SCREENSHOT_URL, screenshotUrl.serialize()); fragment.setArguments(args); return fragment; } private long repoId; - private String screenshotUrl; + private FileV2 screenshot; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); repoId = requireArguments().getLong(ARG_REPO_ID); - screenshotUrl = requireArguments().getString(ARG_SCREENSHOT_URL); + screenshot = FileV2.deserialize(requireArguments().getString(ARG_SCREENSHOT_URL)); } @Nullable @@ -139,7 +141,7 @@ public class ScreenShotsActivity extends AppCompatActivity { View rootView = inflater.inflate(R.layout.activity_screenshots_page, container, false); ImageView screenshotView = (ImageView) rootView.findViewById(R.id.screenshot); - App.loadWithGlide(requireContext(), repoId, screenshotUrl) + App.loadWithGlide(requireContext(), repoId, screenshot) .onlyRetrieveFromCache(!allowDownload) .error(R.drawable.screenshot_placeholder) .fallback(R.drawable.screenshot_placeholder) diff --git a/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsRecyclerViewAdapter.java index a86d3da1b..10fa5d5f1 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsRecyclerViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsRecyclerViewAdapter.java @@ -12,6 +12,7 @@ import com.bumptech.glide.request.RequestOptions; import org.fdroid.fdroid.R; import org.fdroid.fdroid.data.App; +import org.fdroid.index.v2.FileV2; import java.util.List; @@ -20,7 +21,7 @@ import java.util.List; */ class ScreenShotsRecyclerViewAdapter extends RecyclerView.Adapter { private final long repoId; - private final List screenshots; + private final List screenshots; private final RequestOptions displayImageOptions; private final Listener listener; diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/FeatureImage.java b/app/src/main/java/org/fdroid/fdroid/views/apps/FeatureImage.java index 300707434..684374ab4 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/apps/FeatureImage.java +++ b/app/src/main/java/org/fdroid/fdroid/views/apps/FeatureImage.java @@ -9,7 +9,6 @@ import android.graphics.Path; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; -import android.text.TextUtils; import android.util.AttributeSet; import androidx.annotation.ColorInt; @@ -25,6 +24,7 @@ import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; import org.fdroid.fdroid.R; +import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.App; import java.util.Random; @@ -251,11 +251,10 @@ public class FeatureImage extends AppCompatImageView { public void loadImageAndDisplay(App app) { setColour(ContextCompat.getColor(getContext(), R.color.fdroid_blue)); - if (!TextUtils.isEmpty(app.featureGraphic)) { - app.loadWithGlide(getContext(), app.featureGraphic).into(this); + if (app.featureGraphic == null) { + loadImageAndExtractColour(app.loadWithGlide(getContext(), app.iconFile)); } else { - String path = app.getIconPath(getContext()); - loadImageAndExtractColour(app.loadWithGlide(getContext(), path)); + Utils.loadWithGlide(getContext(), app.repoId, app.featureGraphic, this); } } diff --git a/app/src/test/java/org/fdroid/fdroid/RepoUrlsTest.java b/app/src/test/java/org/fdroid/fdroid/RepoUrlsTest.java index 76b33ad2f..b0c7c02dd 100644 --- a/app/src/test/java/org/fdroid/fdroid/RepoUrlsTest.java +++ b/app/src/test/java/org/fdroid/fdroid/RepoUrlsTest.java @@ -20,6 +20,7 @@ package org.fdroid.fdroid; import org.fdroid.fdroid.data.Apk; +import org.fdroid.index.v2.FileV1; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -133,7 +134,7 @@ public class RepoUrlsTest { public void testApkUrls() { testReposWithFile(APK_NAME, tr -> { Apk apk = new Apk(); - apk.apkName = APK_NAME; + apk.apkFile = new FileV1(APK_NAME, "hash", null, null); apk.versionCode = 1; apk.repoAddress = tr.repoUrl; apk.canonicalRepoAddress = tr.repoUrl; diff --git a/app/src/test/java/org/fdroid/fdroid/TestUtils.java b/app/src/test/java/org/fdroid/fdroid/TestUtils.java index 2ab6ad391..f7887a4b3 100644 --- a/app/src/test/java/org/fdroid/fdroid/TestUtils.java +++ b/app/src/test/java/org/fdroid/fdroid/TestUtils.java @@ -2,6 +2,7 @@ package org.fdroid.fdroid; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.App; +import org.fdroid.index.v2.FileV1; import java.io.File; import java.io.FileOutputStream; @@ -41,20 +42,19 @@ public class TestUtils { } } - public static Apk getApk(long appId, int versionCode) { - return getApk(appId, versionCode, "signature", null); + public static Apk getApk(int versionCode) { + return getApk(versionCode, "signature", null); } - public static Apk getApk(long appId, int versionCode, String signature, String releaseChannel) { + public static Apk getApk(int versionCode, String signature, String releaseChannel) { Apk apk = new Apk(); - apk.appId = appId; apk.repoAddress = "http://www.example.com/fdroid/repo"; apk.canonicalRepoAddress = "http://www.example.com/fdroid/repo"; apk.versionCode = versionCode; apk.repoId = 1; apk.versionName = "The good one"; apk.hash = "11111111aaaaaaaa"; - apk.apkName = "Test Apk"; + apk.apkFile = new FileV1("Test Apk", "hash", null, null); apk.size = 10000; apk.compatible = true; apk.sig = signature; diff --git a/app/src/test/java/org/fdroid/fdroid/data/ApkTest.java b/app/src/test/java/org/fdroid/fdroid/data/ApkTest.java index 9ec5c0dc5..a98b6efcb 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ApkTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ApkTest.java @@ -7,6 +7,7 @@ import android.webkit.MimeTypeMap; import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.installer.ApkCache; import org.fdroid.fdroid.nearby.PublicSourceDirProvider; +import org.fdroid.index.v2.FileV1; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -17,6 +18,7 @@ import org.robolectric.shadows.ShadowMimeTypeMap; import java.io.File; import java.io.IOException; +import java.net.URL; import androidx.test.core.app.ApplicationProvider; @@ -41,7 +43,7 @@ public class ApkTest { @Test(expected = IllegalStateException.class) public void testGetMediaInstallPathWithApk() { Apk apk = new Apk(); - apk.apkName = "test.apk"; + apk.apkFile = new FileV1("test.apk", "hash", null, null); apk.repoAddress = "https://example.com/fdroid/repo"; apk.canonicalRepoAddress = "https://example.com/fdroid/repo"; assertTrue(apk.isApk()); @@ -51,7 +53,7 @@ public class ApkTest { @Test public void testGetMediaInstallPathWithOta() throws IOException { Apk apk = new Apk(); - apk.apkName = "org.fdroid.fdroid.privileged.ota_2110.zip"; + apk.apkFile = new FileV1("org.fdroid.fdroid.privileged.ota_2110.zip", "hash", null, null); apk.repoAddress = "https://example.com/fdroid/repo"; apk.canonicalRepoAddress = "https://example.com/fdroid/repo"; assertFalse(apk.isApk()); @@ -63,7 +65,7 @@ public class ApkTest { @Test public void testGetMediaInstallPathWithObf() { Apk apk = new Apk(); - apk.apkName = "Norway_bouvet_europe_2.obf"; + apk.apkFile = new FileV1("Norway_bouvet_europe_2.obf", "hash", null, null); apk.repoAddress = "https://example.com/fdroid/repo"; apk.canonicalRepoAddress = "https://example.com/fdroid/repo"; assertFalse(apk.isApk()); @@ -74,7 +76,7 @@ public class ApkTest { @Test public void testGetMediaInstallPathWithObfZip() throws IOException { Apk apk = new Apk(); - apk.apkName = "Norway_bouvet_europe_2.obf.zip"; + apk.apkFile = new FileV1("Norway_bouvet_europe_2.obf.zip", "hash", null, null); apk.repoAddress = "https://example.com/fdroid/repo"; apk.canonicalRepoAddress = "https://example.com/fdroid/repo"; assertFalse(apk.isApk()); @@ -84,7 +86,8 @@ public class ApkTest { } private void copyResourceFileToCache(Apk apk) throws IOException { - FileUtils.copyInputStreamToFile(getClass().getClassLoader().getResource(apk.apkName).openStream(), + URL res = getClass().getClassLoader().getResource(apk.apkFile.getName()); + FileUtils.copyInputStreamToFile(res.openStream(), ApkCache.getApkDownloadPath(context, apk.getCanonicalUrl())); } } diff --git a/app/src/test/java/org/fdroid/fdroid/data/SuggestedVersionTest.java b/app/src/test/java/org/fdroid/fdroid/data/SuggestedVersionTest.java index a3adffb13..af73be714 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/SuggestedVersionTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/SuggestedVersionTest.java @@ -38,9 +38,9 @@ public class SuggestedVersionTest { App singleApp = TestUtils.getApp(); singleApp.installedVersionCode = 1; singleApp.installedSig = TestUtils.FDROID_SIG; - Apk apk1 = TestUtils.getApk(singleApp.getId(), 1, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); - Apk apk2 = TestUtils.getApk(singleApp.getId(), 2, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); - Apk apk3 = TestUtils.getApk(singleApp.getId(), 3, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_BETA); + Apk apk1 = TestUtils.getApk(1, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); + Apk apk2 = TestUtils.getApk(2, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); + Apk apk3 = TestUtils.getApk(3, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_BETA); List apks = new ArrayList<>(); apks.add(apk3); apks.add(apk2); @@ -57,11 +57,11 @@ public class SuggestedVersionTest { App singleApp = TestUtils.getApp(); singleApp.installedVersionCode = 0; - Apk apk1 = TestUtils.getApk(singleApp.getId(), 1, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); - Apk apk2 = TestUtils.getApk(singleApp.getId(), 2, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); - Apk apk3 = TestUtils.getApk(singleApp.getId(), 3, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); - Apk apk4 = TestUtils.getApk(singleApp.getId(), 4, TestUtils.UPSTREAM_SIG, Apk.RELEASE_CHANNEL_STABLE); - Apk apk5 = TestUtils.getApk(singleApp.getId(), 5, TestUtils.UPSTREAM_SIG, Apk.RELEASE_CHANNEL_BETA); + Apk apk1 = TestUtils.getApk(1, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); + Apk apk2 = TestUtils.getApk(2, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); + Apk apk3 = TestUtils.getApk(3, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); + Apk apk4 = TestUtils.getApk(4, TestUtils.UPSTREAM_SIG, Apk.RELEASE_CHANNEL_STABLE); + Apk apk5 = TestUtils.getApk(5, TestUtils.UPSTREAM_SIG, Apk.RELEASE_CHANNEL_BETA); List apks = new ArrayList<>(); apks.add(apk5); apks.add(apk4); @@ -80,8 +80,8 @@ public class SuggestedVersionTest { assertSuggested(singleApp, apks, 3, Apk.RELEASE_CHANNEL_STABLE); // This adds the "suggestedVersionCode" version of the app, but signed by f-droid. - Apk apk4f = TestUtils.getApk(singleApp.getId(), 4, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); - Apk apk5f = TestUtils.getApk(singleApp.getId(), 5, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_BETA); + Apk apk4f = TestUtils.getApk(4, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE); + Apk apk5f = TestUtils.getApk(5, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_BETA); apks.clear(); apks.add(apk5); apks.add(apk5f); diff --git a/app/src/test/java/org/fdroid/fdroid/installer/FileInstallerTest.java b/app/src/test/java/org/fdroid/fdroid/installer/FileInstallerTest.java index 3512d32fe..fb6d6f720 100644 --- a/app/src/test/java/org/fdroid/fdroid/installer/FileInstallerTest.java +++ b/app/src/test/java/org/fdroid/fdroid/installer/FileInstallerTest.java @@ -4,6 +4,7 @@ import android.content.ContextWrapper; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.data.Apk; +import org.fdroid.index.v2.FileV1; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -17,7 +18,6 @@ import static org.junit.Assert.assertFalse; @RunWith(RobolectricTestRunner.class) public class FileInstallerTest { - public static final String TAG = "FileInstallerTest"; private ContextWrapper context; @@ -31,7 +31,7 @@ public class FileInstallerTest { @Test public void testInstallOtaZip() { Apk apk = new Apk(); - apk.apkName = "org.fdroid.fdroid.privileged.ota_2010.zip"; + apk.apkFile = new FileV1("org.fdroid.fdroid.privileged.ota_2010.zip", "hash", null, null); apk.packageName = "org.fdroid.fdroid.privileged.ota"; apk.versionCode = 2010; assertFalse(apk.isApk()); diff --git a/app/src/test/java/org/fdroid/fdroid/installer/InstallerFactoryTest.java b/app/src/test/java/org/fdroid/fdroid/installer/InstallerFactoryTest.java index cca65c351..8265bfd80 100644 --- a/app/src/test/java/org/fdroid/fdroid/installer/InstallerFactoryTest.java +++ b/app/src/test/java/org/fdroid/fdroid/installer/InstallerFactoryTest.java @@ -4,6 +4,7 @@ import android.content.ContextWrapper; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.data.Apk; +import org.fdroid.index.v2.FileV1; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,7 +29,7 @@ public class InstallerFactoryTest { public void testApkInstallerInstance() { for (String filename : new String[]{"test.apk", "A.APK", "b.ApK"}) { Apk apk = new Apk(); - apk.apkName = filename; + apk.apkFile = new FileV1(filename, "hash", null, null); apk.packageName = "test"; Installer installer = InstallerFactory.create(context, apk); assertEquals(filename + " should use a DefaultInstaller", @@ -41,7 +42,7 @@ public class InstallerFactoryTest { public void testFileInstallerInstance() { for (String filename : new String[]{"org.fdroid.fdroid.privileged.ota_2110.zip", "test.ZIP"}) { Apk apk = new Apk(); - apk.apkName = filename; + apk.apkFile = new FileV1(filename, "hash", null, null); apk.packageName = "cafe0088"; Installer installer = InstallerFactory.create(context, apk); assertEquals("should be a FileInstaller", diff --git a/app/src/test/java/org/fdroid/fdroid/views/AppDetailsAdapterTest.java b/app/src/test/java/org/fdroid/fdroid/views/AppDetailsAdapterTest.java index 9d0ce90b8..7d54cd2d8 100644 --- a/app/src/test/java/org/fdroid/fdroid/views/AppDetailsAdapterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/views/AppDetailsAdapterTest.java @@ -17,6 +17,7 @@ import org.fdroid.fdroid.R; import org.fdroid.fdroid.TestUtils; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.App; +import org.fdroid.index.v2.FileV2; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -57,7 +58,9 @@ public class AppDetailsAdapterTest { @Test public void appWithScreenshots() { App app = TestUtils.getApp(); - app.phoneScreenshots = new String[]{"screenshot1.png", "screenshot2.png"}; + app.phoneScreenshots = new ArrayList<>(2); + app.phoneScreenshots.add(FileV2.fromPath("screenshot1.png")); + app.phoneScreenshots.add(FileV2.fromPath("screenshot2.png")); AppDetailsRecyclerViewAdapter adapter = new AppDetailsRecyclerViewAdapter(context, app, dummyCallbacks); adapter.updateItems(app, Collections.emptyList(), appPrefs); @@ -71,9 +74,9 @@ public class AppDetailsAdapterTest { App app = TestUtils.getApp(); app.preferredSigner = "eaa1d713b9c2a0475234a86d6539f910"; List apks = new ArrayList<>(); - apks.add(TestUtils.getApk(app.getId(), 1)); - apks.add(TestUtils.getApk(app.getId(), 2)); - apks.add(TestUtils.getApk(app.getId(), 3)); + apks.add(TestUtils.getApk(1)); + apks.add(TestUtils.getApk(2)); + apks.add(TestUtils.getApk(3)); app.installedApk = apks.get(0); AppDetailsRecyclerViewAdapter adapter = new AppDetailsRecyclerViewAdapter(context, app, dummyCallbacks);