diff --git a/Parse/src/main/java/com/parse/ParseFile.java b/Parse/src/main/java/com/parse/ParseFile.java index c7399cbec..b580fc571 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,33 @@ public ParseFile(byte[] data, String contentType) { this(null, data, contentType); } + /** + * 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. + * + * @param source + * 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()) + .mimeType(source.readByte() == 1 ? source.readString() : null) + .build()); + } + /* package for tests */ ParseFile(State state) { this.state = state; } @@ -705,4 +735,39 @@ public void cancel() { return json; } + + @Override + public int describeContents() { + return 0; + } + + @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."); + } + 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/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/ParseFileTest.java b/Parse/src/test/java/com/parse/ParseFileTest.java index 8e44c79a3..1328af112 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 = TestHelper.ROBOLECTRIC_SDK_VERSION) 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 } 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