From 321842836cbbd4cb60287f24611f61ca3878927c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= <dominik@dominikschuermann.de>
Date: Mon, 25 Jul 2016 16:52:39 +0200
Subject: [PATCH 1/2] Make App and Apk classes really Parcelable

---
 .../main/java/org/fdroid/fdroid/data/Apk.java | 73 +++++++++++++-
 .../main/java/org/fdroid/fdroid/data/App.java | 99 ++++++++++++++++++-
 2 files changed, 170 insertions(+), 2 deletions(-)

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 7561d2793..eb804fdd7 100644
--- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java
+++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java
@@ -4,6 +4,7 @@ import android.annotation.TargetApi;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.os.Build;
+import android.os.Parcel;
 import android.os.Parcelable;
 
 import org.fdroid.fdroid.Utils;
@@ -13,7 +14,7 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashSet;
 
-public class Apk extends ValueObject implements Comparable<Apk> {
+public class Apk extends ValueObject implements Comparable<Apk>,Parcelable {
 
     // Using only byte-range keeps it only 8-bits in the SQLite database
     public static final int SDK_VERSION_MAX_VALUE = Byte.MAX_VALUE;
@@ -231,4 +232,74 @@ public class Apk extends ValueObject implements Comparable<Apk> {
         return Integer.compare(versionCode, apk.versionCode);
     }
 
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.packageName);
+        dest.writeString(this.versionName);
+        dest.writeInt(this.versionCode);
+        dest.writeInt(this.size);
+        dest.writeLong(this.repo);
+        dest.writeString(this.hash);
+        dest.writeString(this.hashType);
+        dest.writeInt(this.minSdkVersion);
+        dest.writeInt(this.targetSdkVersion);
+        dest.writeInt(this.maxSdkVersion);
+        dest.writeLong(this.added != null ? this.added.getTime() : -1);
+        dest.writeStringArray(this.permissions);
+        dest.writeStringArray(this.features);
+        dest.writeStringArray(this.nativecode);
+        dest.writeString(this.sig);
+        dest.writeByte(this.compatible ? (byte) 1 : (byte) 0);
+        dest.writeString(this.apkName);
+        dest.writeSerializable(this.installedFile);
+        dest.writeString(this.srcname);
+        dest.writeInt(this.repoVersion);
+        dest.writeString(this.repoAddress);
+        dest.writeStringArray(this.incompatibleReasons);
+        dest.writeLong(this.appId);
+    }
+
+    protected Apk(Parcel in) {
+        this.packageName = in.readString();
+        this.versionName = in.readString();
+        this.versionCode = in.readInt();
+        this.size = in.readInt();
+        this.repo = in.readLong();
+        this.hash = in.readString();
+        this.hashType = in.readString();
+        this.minSdkVersion = in.readInt();
+        this.targetSdkVersion = in.readInt();
+        this.maxSdkVersion = in.readInt();
+        long tmpAdded = in.readLong();
+        this.added = tmpAdded == -1 ? null : new Date(tmpAdded);
+        this.permissions = in.createStringArray();
+        this.features = in.createStringArray();
+        this.nativecode = in.createStringArray();
+        this.sig = in.readString();
+        this.compatible = in.readByte() != 0;
+        this.apkName = in.readString();
+        this.installedFile = (SanitizedFile) in.readSerializable();
+        this.srcname = in.readString();
+        this.repoVersion = in.readInt();
+        this.repoAddress = in.readString();
+        this.incompatibleReasons = in.createStringArray();
+        this.appId = in.readLong();
+    }
+
+    public static final Parcelable.Creator<Apk> CREATOR = new Parcelable.Creator<Apk>() {
+        @Override
+        public Apk createFromParcel(Parcel source) {
+            return new Apk(source);
+        }
+
+        @Override
+        public Apk[] newArray(int size) {
+            return new Apk[size];
+        }
+    };
 }
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 a071393b5..ec845e5b6 100644
--- a/app/src/main/java/org/fdroid/fdroid/data/App.java
+++ b/app/src/main/java/org/fdroid/fdroid/data/App.java
@@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
 import android.content.res.XmlResourceParser;
 import android.database.Cursor;
+import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
@@ -34,7 +35,7 @@ import java.util.regex.Pattern;
 
 import org.fdroid.fdroid.data.Schema.AppTable.Cols;
 
-public class App extends ValueObject implements Comparable<App> {
+public class App extends ValueObject implements Comparable<App>,Parcelable {
 
     private static final String TAG = "App";
 
@@ -560,4 +561,100 @@ public class App extends ValueObject implements Comparable<App> {
     public long getId() {
         return id;
     }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte(this.compatible ? (byte) 1 : (byte) 0);
+        dest.writeString(this.packageName);
+        dest.writeString(this.name);
+        dest.writeString(this.summary);
+        dest.writeString(this.icon);
+        dest.writeString(this.description);
+        dest.writeString(this.license);
+        dest.writeString(this.author);
+        dest.writeString(this.email);
+        dest.writeString(this.webURL);
+        dest.writeString(this.trackerURL);
+        dest.writeString(this.sourceURL);
+        dest.writeString(this.changelogURL);
+        dest.writeString(this.donateURL);
+        dest.writeString(this.bitcoinAddr);
+        dest.writeString(this.litecoinAddr);
+        dest.writeString(this.flattrID);
+        dest.writeString(this.upstreamVersionName);
+        dest.writeInt(this.upstreamVersionCode);
+        dest.writeString(this.suggestedVersionName);
+        dest.writeInt(this.suggestedVersionCode);
+        dest.writeLong(this.added != null ? this.added.getTime() : -1);
+        dest.writeLong(this.lastUpdated != null ? this.lastUpdated.getTime() : -1);
+        dest.writeStringArray(this.categories);
+        dest.writeStringArray(this.antiFeatures);
+        dest.writeStringArray(this.requirements);
+        dest.writeByte(this.ignoreAllUpdates ? (byte) 1 : (byte) 0);
+        dest.writeInt(this.ignoreThisUpdate);
+        dest.writeString(this.iconUrl);
+        dest.writeString(this.iconUrlLarge);
+        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) {
+        this.compatible = in.readByte() != 0;
+        this.packageName = in.readString();
+        this.name = in.readString();
+        this.summary = in.readString();
+        this.icon = in.readString();
+        this.description = in.readString();
+        this.license = in.readString();
+        this.author = in.readString();
+        this.email = in.readString();
+        this.webURL = in.readString();
+        this.trackerURL = in.readString();
+        this.sourceURL = in.readString();
+        this.changelogURL = in.readString();
+        this.donateURL = in.readString();
+        this.bitcoinAddr = in.readString();
+        this.litecoinAddr = in.readString();
+        this.flattrID = in.readString();
+        this.upstreamVersionName = in.readString();
+        this.upstreamVersionCode = in.readInt();
+        this.suggestedVersionName = in.readString();
+        this.suggestedVersionCode = in.readInt();
+        long tmpAdded = in.readLong();
+        this.added = tmpAdded == -1 ? null : new Date(tmpAdded);
+        long tmpLastUpdated = in.readLong();
+        this.lastUpdated = tmpLastUpdated == -1 ? null : new Date(tmpLastUpdated);
+        this.categories = in.createStringArray();
+        this.antiFeatures = in.createStringArray();
+        this.requirements = in.createStringArray();
+        this.ignoreAllUpdates = in.readByte() != 0;
+        this.ignoreThisUpdate = in.readInt();
+        this.iconUrl = in.readString();
+        this.iconUrlLarge = in.readString();
+        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<App> CREATOR = new Parcelable.Creator<App>() {
+        @Override
+        public App createFromParcel(Parcel source) {
+            return new App(source);
+        }
+
+        @Override
+        public App[] newArray(int size) {
+            return new App[size];
+        }
+    };
 }

From 2e92dc941bf17bf5b581d2a9a9ec78a32c8848fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= <dominik@dominikschuermann.de>
Date: Mon, 25 Jul 2016 16:59:53 +0200
Subject: [PATCH 2/2] Use App/Apk parceling instead of ContentVals

The usage of ContentValues to send App/Apk objects
to services was an hack in my opinion.
This hack broke in https://gitlab.com/fdroid/fdroidclient/merge_requests/359
where the packageName has been removed from the
toContentValues() method, which leads to NPEs in
the services.
---
 app/src/main/java/org/fdroid/fdroid/data/Apk.java         | 6 +-----
 app/src/main/java/org/fdroid/fdroid/data/App.java         | 6 +-----
 .../fdroid/fdroid/installer/InstallManagerService.java    | 8 ++++----
 .../org/fdroid/fdroid/installer/InstallerService.java     | 6 ++----
 4 files changed, 8 insertions(+), 18 deletions(-)

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 eb804fdd7..2dcb65273 100644
--- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java
+++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java
@@ -14,7 +14,7 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashSet;
 
-public class Apk extends ValueObject implements Comparable<Apk>,Parcelable {
+public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
 
     // Using only byte-range keeps it only 8-bits in the SQLite database
     public static final int SDK_VERSION_MAX_VALUE = Byte.MAX_VALUE;
@@ -70,10 +70,6 @@ public class Apk extends ValueObject implements Comparable<Apk>,Parcelable {
     public Apk() {
     }
 
-    public Apk(Parcelable parcelable) {
-        this(new ContentValuesCursor((ContentValues) parcelable));
-    }
-
     public Apk(Cursor cursor) {
 
         checkCursorPosition(cursor);
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 ec845e5b6..759fbcbde 100644
--- a/app/src/main/java/org/fdroid/fdroid/data/App.java
+++ b/app/src/main/java/org/fdroid/fdroid/data/App.java
@@ -35,7 +35,7 @@ import java.util.regex.Pattern;
 
 import org.fdroid.fdroid.data.Schema.AppTable.Cols;
 
-public class App extends ValueObject implements Comparable<App>,Parcelable {
+public class App extends ValueObject implements Comparable<App>, Parcelable {
 
     private static final String TAG = "App";
 
@@ -145,10 +145,6 @@ public class App extends ValueObject implements Comparable<App>,Parcelable {
     public App() {
     }
 
-    public App(Parcelable parcelable) {
-        this(new ContentValuesCursor((ContentValues) parcelable));
-    }
-
     public App(Cursor cursor) {
 
         checkCursorPosition(cursor);
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 a838fc3e1..631c4edee 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java
@@ -151,8 +151,8 @@ public class InstallManagerService extends Service {
             return START_NOT_STICKY;
         }
 
-        App app = new App(intent.getParcelableExtra(EXTRA_APP));
-        Apk apk = new Apk(intent.getParcelableExtra(EXTRA_APK));
+        App app = intent.getParcelableExtra(EXTRA_APP);
+        Apk apk = intent.getParcelableExtra(EXTRA_APK);
         addToActive(urlString, app, apk);
 
         NotificationCompat.Builder builder = createNotificationBuilder(urlString, apk);
@@ -454,8 +454,8 @@ public class InstallManagerService extends Service {
         Intent intent = new Intent(context, InstallManagerService.class);
         intent.setAction(ACTION_INSTALL);
         intent.setData(Uri.parse(urlString));
-        intent.putExtra(EXTRA_APP, app.toContentValues());
-        intent.putExtra(EXTRA_APK, apk.toContentValues());
+        intent.putExtra(EXTRA_APP, app);
+        intent.putExtra(EXTRA_APK, apk);
         context.startService(intent);
     }
 
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallerService.java
index e12282770..7ceee54c2 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/InstallerService.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallerService.java
@@ -23,7 +23,6 @@ import android.app.IntentService;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
-import android.os.Parcelable;
 
 import org.fdroid.fdroid.data.Apk;
 
@@ -52,8 +51,7 @@ public class InstallerService extends IntentService {
 
     @Override
     protected void onHandleIntent(Intent intent) {
-        Parcelable apkParcel = intent.getParcelableExtra(Installer.EXTRA_APK);
-        Apk apk = apkParcel == null ? null : new Apk(apkParcel);
+        Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
 
         Installer installer = InstallerFactory.create(this, apk);
 
@@ -80,7 +78,7 @@ public class InstallerService extends IntentService {
         intent.setAction(ACTION_INSTALL);
         intent.setData(localApkUri);
         intent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri);
-        intent.putExtra(Installer.EXTRA_APK, apk.toContentValues());
+        intent.putExtra(Installer.EXTRA_APK, apk);
         context.startService(intent);
     }