From 92a9c6accf2cdce933177bbe02063be3f1cc4228 Mon Sep 17 00:00:00 2001 From: miav Date: Mon, 3 Apr 2017 18:25:37 +0200 Subject: [PATCH 1/3] Implementing Parcelable in ParseFile --- Parse/src/main/java/com/parse/ParseFile.java | 52 ++++++++++++++++++- .../test/java/com/parse/ParseFileTest.java | 34 ++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/Parse/src/main/java/com/parse/ParseFile.java b/Parse/src/main/java/com/parse/ParseFile.java index c7399cbec..51049fe80 100644 --- a/Parse/src/main/java/com/parse/ParseFile.java +++ b/Parse/src/main/java/com/parse/ParseFile.java @@ -8,6 +8,9 @@ */ package com.parse; +import android.os.Parcel; +import android.os.Parcelable; + import org.json.JSONException; import org.json.JSONObject; @@ -40,7 +43,7 @@ * object.save(); * */ -public class ParseFile { +public class ParseFile implements Parcelable { /* package for tests */ static ParseFileController getFileController() { return ParseCorePlugins.getInstance().getFileController(); @@ -221,6 +224,22 @@ public ParseFile(byte[] data, String contentType) { this(null, data, contentType); } + /** + * Creates a new file instance from a Parcel {@code source}. This is used when unparceling + * a non-dirty ParseFile. Subclasses that need Parcelable behavior should provide their own + * {@link android.os.Parcelable.Creator} and override this constructor. + * + * @param source + * the source Parcel + */ + protected ParseFile(Parcel source) { + this(new State.Builder() + .url(source.readString()) + .name(source.readString()) + .mimeType(source.readByte() == 1 ? source.readString() : null) + .build()); + } + /* package for tests */ ParseFile(State state) { this.state = state; } @@ -705,4 +724,35 @@ public void cancel() { return json; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (isDirty()) { + throw new RuntimeException("Unable to parcel an unsaved ParseFile."); + } + dest.writeString(getUrl()); // Not null + dest.writeString(getName()); // Not null + String type = state.mimeType(); // Nullable + dest.writeByte(type != null ? (byte) 1 : 0); + if (type != null) { + dest.writeString(type); + } + } + + public final static Creator CREATOR = new Creator() { + @Override + public ParseFile createFromParcel(Parcel source) { + return new ParseFile(source); + } + + @Override + public ParseFile[] newArray(int size) { + return new ParseFile[size]; + } + }; } diff --git a/Parse/src/test/java/com/parse/ParseFileTest.java b/Parse/src/test/java/com/parse/ParseFileTest.java index 8e44c79a3..5a2a21275 100644 --- a/Parse/src/test/java/com/parse/ParseFileTest.java +++ b/Parse/src/test/java/com/parse/ParseFileTest.java @@ -8,13 +8,18 @@ */ package com.parse; +import android.os.Parcel; + import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Matchers; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; import java.io.File; import java.io.InputStream; @@ -36,6 +41,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +@RunWith(RobolectricTestRunner.class) +@Config(constants = BuildConfig.class, sdk = 23) public class ParseFileTest { @Rule @@ -492,6 +499,33 @@ public void testCancel() { } } + @Test + public void testParcelable() { + String mime = "mime"; + String name = "name"; + String url = "url"; + ParseFile file = new ParseFile(new ParseFile.State.Builder() + .name(name) + .mimeType(mime) + .url(url) + .build()); + Parcel parcel = Parcel.obtain(); + file.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + file = ParseFile.CREATOR.createFromParcel(parcel); + assertEquals(file.getName(), name); + assertEquals(file.getUrl(), url); + assertEquals(file.getState().mimeType(), mime); + assertFalse(file.isDirty()); + } + + @Test( expected = RuntimeException.class ) + public void testDontParcelIfDirty() { + ParseFile file = new ParseFile(new ParseFile.State.Builder().build()); + Parcel parcel = Parcel.obtain(); + file.writeToParcel(parcel, 0); + } + // TODO(grantland): testEncode // TODO(grantland): testDecode } From 081b8ec86851f63aa9eb75d05b9e2467a92979b5 Mon Sep 17 00:00:00 2001 From: miav Date: Thu, 20 Apr 2017 00:47:52 +0200 Subject: [PATCH 2/3] Update encoder/decoder --- Parse/src/main/java/com/parse/ParseFile.java | 17 ++++++++++++++++- .../main/java/com/parse/ParseParcelDecoder.java | 3 +++ .../main/java/com/parse/ParseParcelEncoder.java | 4 +++- .../test/java/com/parse/ParseObjectTest.java | 6 +++++- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Parse/src/main/java/com/parse/ParseFile.java b/Parse/src/main/java/com/parse/ParseFile.java index 51049fe80..b580fc571 100644 --- a/Parse/src/main/java/com/parse/ParseFile.java +++ b/Parse/src/main/java/com/parse/ParseFile.java @@ -225,7 +225,7 @@ public ParseFile(byte[] data, String contentType) { } /** - * Creates a new file instance from a Parcel {@code source}. This is used when unparceling + * Creates a new file instance from a {@link Parcel} source. This is used when unparceling * a non-dirty ParseFile. Subclasses that need Parcelable behavior should provide their own * {@link android.os.Parcelable.Creator} and override this constructor. * @@ -233,6 +233,17 @@ public ParseFile(byte[] data, String contentType) { * the source Parcel */ protected ParseFile(Parcel source) { + this(source, ParseParcelDecoder.get()); + } + + /** + * Creates a new file instance from a {@link Parcel} using the given {@link ParseParcelDecoder}. + * The decoder is currently unused, but it might be in the future, plus this is the pattern we + * are using in parcelable classes. + * @param source the parcel + * @param decoder the decoder + */ + ParseFile(Parcel source, ParseParcelDecoder decoder) { this(new State.Builder() .url(source.readString()) .name(source.readString()) @@ -732,6 +743,10 @@ public int describeContents() { @Override public void writeToParcel(Parcel dest, int flags) { + writeToParcel(dest, ParseParcelEncoder.get()); + } + + void writeToParcel(Parcel dest, ParseParcelEncoder encoder) { if (isDirty()) { throw new RuntimeException("Unable to parcel an unsaved ParseFile."); } diff --git a/Parse/src/main/java/com/parse/ParseParcelDecoder.java b/Parse/src/main/java/com/parse/ParseParcelDecoder.java index cacc2344a..ea22923fa 100644 --- a/Parse/src/main/java/com/parse/ParseParcelDecoder.java +++ b/Parse/src/main/java/com/parse/ParseParcelDecoder.java @@ -59,6 +59,9 @@ public Object decode(Parcel source) { case ParseParcelEncoder.TYPE_OP: return ParseFieldOperations.decode(source, this); + case ParseParcelEncoder.TYPE_FILE: + return new ParseFile(source, this); + case ParseParcelEncoder.TYPE_ACL: return new ParseACL(source, this); diff --git a/Parse/src/main/java/com/parse/ParseParcelEncoder.java b/Parse/src/main/java/com/parse/ParseParcelEncoder.java index f9124b91a..c4d1be572 100644 --- a/Parse/src/main/java/com/parse/ParseParcelEncoder.java +++ b/Parse/src/main/java/com/parse/ParseParcelEncoder.java @@ -53,6 +53,7 @@ private static boolean isValidType(Object value) { /* package */ final static String TYPE_NULL = "Null"; /* package */ final static String TYPE_NATIVE = "Native"; /* package */ final static String TYPE_OP = "Operation"; + /* package */ final static String TYPE_FILE = "ParseFile"; public void encode(Object object, Parcel dest) { try { @@ -75,7 +76,8 @@ public void encode(Object object, Parcel dest) { ((ParseFieldOperation) object).encode(dest, this); } else if (object instanceof ParseFile) { - throw new IllegalArgumentException("Not supported yet"); + dest.writeString(TYPE_FILE); + ((ParseFile) object).writeToParcel(dest, this); } else if (object instanceof ParseGeoPoint) { throw new IllegalArgumentException("Not supported yet"); diff --git a/Parse/src/test/java/com/parse/ParseObjectTest.java b/Parse/src/test/java/com/parse/ParseObjectTest.java index 1123adb26..4c1ac11bf 100644 --- a/Parse/src/test/java/com/parse/ParseObjectTest.java +++ b/Parse/src/test/java/com/parse/ParseObjectTest.java @@ -497,7 +497,7 @@ public void testGetLongWithWrongValue() throws Exception { @Test public void testParcelable() throws Exception { - // TODO test ParseGeoPoint and ParseFile after merge + // TODO test ParseGeoPoint after merge ParseObject object = ParseObject.createWithoutData("Test", "objectId"); object.isDeleted = true; object.put("long", 200L); @@ -528,6 +528,9 @@ public void testParcelable() throws Exception { ParseRelation rel = new ParseRelation<>(object, "relation"); rel.add(related); object.put("relation", rel); + // File + ParseFile file = new ParseFile(new ParseFile.State.Builder().url("fileUrl").build()); + object.put("file", file); Parcel parcel = Parcel.obtain(); object.writeToParcel(parcel, 0); @@ -553,6 +556,7 @@ public void testParcelable() throws Exception { assertEquals(newRel.getKey(), rel.getKey()); assertEquals(newRel.getKnownObjects().size(), rel.getKnownObjects().size()); newRel.hasKnownObject(related); + assertEquals(newObject.getParseFile("file").getUrl(), object.getParseFile("file").getUrl()); } @Test From 51523691ec4bee80faae026c1c5973e4fbe3d345 Mon Sep 17 00:00:00 2001 From: miav Date: Thu, 20 Apr 2017 00:53:08 +0200 Subject: [PATCH 3/3] Fix tests --- Parse/src/test/java/com/parse/ParseFileTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parse/src/test/java/com/parse/ParseFileTest.java b/Parse/src/test/java/com/parse/ParseFileTest.java index 5a2a21275..1328af112 100644 --- a/Parse/src/test/java/com/parse/ParseFileTest.java +++ b/Parse/src/test/java/com/parse/ParseFileTest.java @@ -42,7 +42,7 @@ import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = 23) +@Config(constants = BuildConfig.class, sdk = TestHelper.ROBOLECTRIC_SDK_VERSION) public class ParseFileTest { @Rule