diff --git a/Example/src/main/AndroidManifest.xml b/Example/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0ce1391
--- /dev/null
+++ b/Example/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Example/src/main/java/com/parse/example/MainActivity.java b/Example/src/main/java/com/parse/example/MainActivity.java
new file mode 100644
index 0000000..9e949e4
--- /dev/null
+++ b/Example/src/main/java/com/parse/example/MainActivity.java
@@ -0,0 +1,84 @@
+package com.parse.example;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+
+import com.parse.GetCallback;
+import com.parse.Parse;
+import com.parse.ParseException;
+import com.parse.ParseLiveQueryClient;
+import com.parse.ParseObject;
+import com.parse.ParseQuery;
+import com.parse.SubscriptionHandling;
+import com.parse.interceptors.ParseLogInterceptor;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class MainActivity extends AppCompatActivity {
+
+ String URL = "http://192.168.3.9:1337/parse/";
+ String wsURL = "ws://192.168.3.9:1337/parse/";
+ String applicationId = "mytest";
+ String DEBUG_TAG = "debug";
+
+ Room mRoom;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ ParseObject.registerSubclass(Room.class);
+ ParseObject.registerSubclass(Message.class);
+
+ Parse.setLogLevel(Parse.LOG_LEVEL_DEBUG);
+ Parse.initialize(new Parse.Configuration.Builder(this)
+ .applicationId(applicationId) // should correspond to APP_ID env variable
+ .clientKey("clientKey") // set explicitly blank unless clientKey is configured on Parse server
+ .addNetworkInterceptor(new ParseLogInterceptor())
+ .server(URL).build());
+
+ ParseQuery roomParseQuery = ParseQuery.getQuery(Room.class);
+ // fixme - why isn't it retrieving
+ roomParseQuery.whereEqualTo("name", "test");
+ roomParseQuery.getFirstInBackground(new GetCallback() {
+ @Override
+ public void done(Room room, ParseException e) {
+ if (e != null) {
+ Log.d(DEBUG_TAG, "Found exception" + e);
+ e.printStackTrace();
+ } else {
+ Log.d(DEBUG_TAG, "Found room: " + room);
+ }
+ mRoom = room;
+
+ ParseLiveQueryClient parseLiveQueryClient = ParseLiveQueryClient.Factory.getClient();
+
+ if (parseLiveQueryClient != null) {
+ // op=subscribe, className=Message, roomName=test, requestId=1
+ // op=subscribe, className=Message, roomName=null, requestId=1, order=createdAt
+ ParseQuery parseQuery = ParseQuery.getQuery(Message.class);
+ // FIXME
+ parseQuery.whereEqualTo("roomName", "test");
+
+ // FIXME (rhu) - parse query hates created at
+// parseQuery.orderByAscending("createdAt");
+
+ SubscriptionHandling subscriptionHandling = parseLiveQueryClient
+ .subscribe(parseQuery);
+ subscriptionHandling.handleEvent(SubscriptionHandling.Event.CREATE,
+ new SubscriptionHandling.HandleEventCallback() {
+ @Override
+ public void onEvent(ParseQuery query, Message object) {
+ Log.d(DEBUG_TAG, "Message" + object);
+ }
+ });
+ }
+ }
+ });
+
+
+ }
+}
diff --git a/Example/src/main/java/com/parse/example/Message.java b/Example/src/main/java/com/parse/example/Message.java
new file mode 100644
index 0000000..43dd9f3
--- /dev/null
+++ b/Example/src/main/java/com/parse/example/Message.java
@@ -0,0 +1,56 @@
+package com.parse.example;
+
+
+import com.parse.ParseClassName;
+import com.parse.ParseObject;
+import com.parse.ParseUser;
+
+@ParseClassName("Message")
+public class Message extends ParseObject {
+
+ private final String PARSE_USER_KEY = "parseUser";
+ private final String AUTHOR_KEY = "author";
+ private final String MESSAGE_KEY = "message";
+ private final String ROOM_KEY = "room";
+
+ public Message() {
+
+ }
+
+ public ParseUser getParseUser() {
+ return getParseUser(PARSE_USER_KEY);
+ }
+
+ public void setParseUser(ParseUser parseUser) {
+ put(PARSE_USER_KEY, parseUser);
+ }
+
+ public String getAuthorName() {
+ return getString(AUTHOR_KEY);
+ }
+
+ public void setAuthorName(String authorName) {
+ put(AUTHOR_KEY, authorName);
+ }
+
+ public String getMessage() {
+ return getString(MESSAGE_KEY);
+ }
+
+ public void setMessage(String message) {
+ put(MESSAGE_KEY, message);
+ }
+
+ public Room getRoom() {
+ return (Room) get(ROOM_KEY);
+ }
+
+ public void setRoom(Room room) {
+ put(ROOM_KEY, room);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("objectId=%s: message=%s room=%s", getObjectId(), getMessage(), getRoom());
+ }
+}
diff --git a/Example/src/main/java/com/parse/example/Room.java b/Example/src/main/java/com/parse/example/Room.java
new file mode 100644
index 0000000..dc34918
--- /dev/null
+++ b/Example/src/main/java/com/parse/example/Room.java
@@ -0,0 +1,29 @@
+package com.parse.example;
+
+import com.parse.ParseClassName;
+import com.parse.ParseObject;
+
+@ParseClassName("Room")
+public class Room extends ParseObject {
+
+ private final String NAME_KEY = "name";
+
+ public Room() {
+
+ }
+
+ String name;
+
+ public String getName() {
+ return getString(NAME_KEY);
+ }
+
+ public void setName(String name) {
+ put(NAME_KEY, name);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("objectId=%s: name=%s", getObjectId(), getName());
+ }
+}
diff --git a/Example/src/main/res/layout/activity_main.xml b/Example/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..4eca4f1
--- /dev/null
+++ b/Example/src/main/res/layout/activity_main.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/Example/src/main/res/mipmap-hdpi/ic_launcher.png b/Example/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Example/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Example/src/main/res/mipmap-hdpi/ic_launcher_round.png b/Example/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9a078e3
Binary files /dev/null and b/Example/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/Example/src/main/res/mipmap-mdpi/ic_launcher.png b/Example/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Example/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Example/src/main/res/mipmap-mdpi/ic_launcher_round.png b/Example/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..efc028a
Binary files /dev/null and b/Example/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/Example/src/main/res/mipmap-xhdpi/ic_launcher.png b/Example/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Example/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Example/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/Example/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..3af2608
Binary files /dev/null and b/Example/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/Example/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Example/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Example/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Example/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Example/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9bec2e6
Binary files /dev/null and b/Example/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/Example/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Example/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Example/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..34947cd
Binary files /dev/null and b/Example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/Example/src/main/res/values/colors.xml b/Example/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Example/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Example/src/main/res/values/strings.xml b/Example/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0057fcb
--- /dev/null
+++ b/Example/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Example
+
diff --git a/Example/src/main/res/values/styles.xml b/Example/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Example/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/ParseLiveQuery/src/main/java/com/parse/OkHttp3WebSocketClient.java b/ParseLiveQuery/src/main/java/com/parse/OkHttp3WebSocketClient.java
new file mode 100644
index 0000000..f8439ab
--- /dev/null
+++ b/ParseLiveQuery/src/main/java/com/parse/OkHttp3WebSocketClient.java
@@ -0,0 +1,104 @@
+package com.parse;
+
+import android.util.Log;
+
+import java.net.URI;
+import java.util.Locale;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.WebSocketListener;
+import okio.ByteString;
+
+/* package */ class OkHttp3WebSocketClient implements WebSocketClient {
+
+ private static final String LOG_TAG = "OkHttpWebSocketClient";
+
+ private final WebSocketClientCallback webSocketClientCallback;
+ private okhttp3.WebSocket webSocket;
+ private State state = State.NONE;
+ private OkHttpClient client;
+ private final String url;
+ private final int STATUS_CODE = 200;
+ private final String CLOSING_MSG = "User invoked close";
+
+ private final WebSocketListener handler = new WebSocketListener() {
+ @Override
+ public void onOpen(okhttp3.WebSocket webSocket, Response response) {
+ setState(State.CONNECTED);
+ webSocketClientCallback.onOpen();
+ }
+
+ @Override
+ public void onMessage(okhttp3.WebSocket webSocket, String text) {
+ webSocketClientCallback.onMessage(text);
+ }
+
+ @Override
+ public void onMessage(okhttp3.WebSocket webSocket, ByteString bytes) {
+ Log.w(LOG_TAG, String.format(Locale.US, "Socket got into inconsistent state and received %s instead.", bytes.toString()));
+ }
+
+ @Override
+ public void onClosed(okhttp3.WebSocket webSocket, int code, String reason) {
+ setState(State.DISCONNECTED);
+ webSocketClientCallback.onClose();
+ }
+
+ @Override
+ public void onFailure(okhttp3.WebSocket webSocket, Throwable t, Response response) {
+ webSocketClientCallback.onError(t);
+ }
+ };
+
+ private OkHttp3WebSocketClient(WebSocketClientCallback webSocketClientCallback, URI hostUrl) {
+ this.webSocketClientCallback = webSocketClientCallback;
+ client = new OkHttpClient();
+ url = hostUrl.toString();
+ }
+
+ @Override
+ public synchronized void open() {
+ if (State.NONE == state) {
+ // OkHttp3 connects as soon as the socket is created so do it here.
+ Request request = new Request.Builder()
+ .url(url)
+ .build();
+
+ webSocket = client.newWebSocket(request, handler);
+ setState(State.CONNECTING);
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ setState(State.DISCONNECTING);
+ webSocket.close(STATUS_CODE, CLOSING_MSG);
+ }
+
+ @Override
+ public void send(String message) {
+ if (state == State.CONNECTED) {
+ webSocket.send(message);
+ }
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ private synchronized void setState(State newState) {
+ this.state = newState;
+ this.webSocketClientCallback.stateChanged();
+ }
+
+ /* package */ static class OkHttp3SocketClientFactory implements WebSocketClientFactory {
+ @Override
+ public WebSocketClient createInstance(WebSocketClientCallback webSocketClientCallback, URI hostUrl) {
+ return new OkHttp3WebSocketClient(webSocketClientCallback, hostUrl);
+ }
+ }
+
+}
diff --git a/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java b/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java
index e9387d4..8675207 100644
--- a/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java
+++ b/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java
@@ -27,7 +27,7 @@
// TODO: add support for fields
// https://github.com/ParsePlatform/parse-server/issues/3671
-
+
PointerEncoder pointerEncoder = PointerEncoder.get();
queryJsonObject.put("where", pointerEncoder.encode(state.constraints()));
diff --git a/README.md b/README.md
index 2361cd9..825b450 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Download [the latest JAR][latest] or define in Gradle:
```groovy
dependencies {
- compile 'com.parse:parse-livequery-android:1.0.0'
+ compile 'com.parse:parse-livequery-android:1.0.1'
}
```
@@ -39,6 +39,22 @@ ParseLiveQueryClient parseLiveQueryClient = ParseLiveQueryClient.Factory.getClie
### Creating Live Queries
+If you wish to pass in your own OkHttpClient instance for troubleshooting or custom configs, you can instantiate the client as follows:
+
+```java
+ParseLiveQueryClient parseLiveQueryClient =
+ParseLiveQueryClient.Factory.getClient(new OkHttp3SocketClientFactory(new OkHttpClient()));
+```
+
+The URL is determined by the Parse initialization, but you can override by specifying a `URI` object:
+
+```java
+ParseLiveQueryClient parseLiveQueryClient =
+ParseLiveQueryClient.Factory.getClient(new URI("wss://myparseinstance.com"));
+```
+
+Note: The expected protocol for URI is `ws` instead of `http`, like in this example: `URI("ws://192.168.0.1:1337/1")`.
+
Live querying depends on creating a subscription to a `ParseQuery`:
```java
diff --git a/settings.gradle b/settings.gradle
index 467a08b..ece2b2e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':ParseLiveQuery'
+include ':ParseLiveQuery', ':Example'