(
+ @SerializedName("code")
+ val code: Int = 0, // 200
+ @SerializedName("data")
+ val `data`: T?,
+ @SerializedName("msg")
+ val msg: String = "" // Successful request
+
+)
\ No newline at end of file
diff --git a/simple/src/main/res/drawable-v24/ic_launcher_foreground.xml b/simple/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..c7bd21d
--- /dev/null
+++ b/simple/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/simple/src/main/res/drawable/ic_launcher_background.xml b/simple/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..d81b113
--- /dev/null
+++ b/simple/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/simple/src/main/res/layout/activity_simple_kchart_landscape.xml b/simple/src/main/res/layout/activity_simple_kchart_landscape.xml
new file mode 100644
index 0000000..6c8dceb
--- /dev/null
+++ b/simple/src/main/res/layout/activity_simple_kchart_landscape.xml
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/simple/src/main/res/layout/activity_simple_kchart_portrait.xml b/simple/src/main/res/layout/activity_simple_kchart_portrait.xml
new file mode 100644
index 0000000..d48a7b2
--- /dev/null
+++ b/simple/src/main/res/layout/activity_simple_kchart_portrait.xml
@@ -0,0 +1,245 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/simple/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/simple/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/simple/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/simple/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/simple/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/simple/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/simple/src/main/res/mipmap-hdpi/ic_launcher.png b/simple/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..898f3ed
Binary files /dev/null and b/simple/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/simple/src/main/res/mipmap-hdpi/ic_launcher_round.png b/simple/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dffca36
Binary files /dev/null and b/simple/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/simple/src/main/res/mipmap-mdpi/ic_launcher.png b/simple/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..64ba76f
Binary files /dev/null and b/simple/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/simple/src/main/res/mipmap-mdpi/ic_launcher_round.png b/simple/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dae5e08
Binary files /dev/null and b/simple/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/simple/src/main/res/mipmap-xhdpi/ic_launcher.png b/simple/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..e5ed465
Binary files /dev/null and b/simple/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/simple/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/simple/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..14ed0af
Binary files /dev/null and b/simple/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/simple/src/main/res/mipmap-xxhdpi/ic_launcher.png b/simple/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b0907ca
Binary files /dev/null and b/simple/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/simple/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/simple/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..d8ae031
Binary files /dev/null and b/simple/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2c18de9
Binary files /dev/null and b/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/simple/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/simple/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..beed3cd
Binary files /dev/null and b/simple/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/simple/src/main/res/values/colors.xml b/simple/src/main/res/values/colors.xml
new file mode 100644
index 0000000..585cf3c
--- /dev/null
+++ b/simple/src/main/res/values/colors.xml
@@ -0,0 +1,11 @@
+
+
+ #008577
+ #00574B
+ #D81B60
+
+ #1882D4
+ #6D87A8
+
+
+
diff --git a/simple/src/main/res/values/strings.xml b/simple/src/main/res/values/strings.xml
new file mode 100644
index 0000000..340e1cf
--- /dev/null
+++ b/simple/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ KChart
+
diff --git a/simple/src/main/res/values/styles.xml b/simple/src/main/res/values/styles.xml
new file mode 100644
index 0000000..6751f80
--- /dev/null
+++ b/simple/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/simple/src/test/java/pro/udax/app/ExampleUnitTest.kt b/simple/src/test/java/pro/udax/app/ExampleUnitTest.kt
new file mode 100644
index 0000000..afb5415
--- /dev/null
+++ b/simple/src/test/java/pro/udax/app/ExampleUnitTest.kt
@@ -0,0 +1,16 @@
+package pro.udax.app
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
diff --git a/stomped-lib/.gitignore b/stomped-lib/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/stomped-lib/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/stomped-lib/build.gradle b/stomped-lib/build.gradle
new file mode 100644
index 0000000..ae4edef
--- /dev/null
+++ b/stomped-lib/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 28
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compileOnly 'androidx.appcompat:appcompat:1.0.0'
+ compileOnly 'com.squareup.okhttp3:okhttp:3.12.0'
+ testImplementation 'junit:junit:4.12'
+}
diff --git a/stomped-lib/proguard-rules.pro b/stomped-lib/proguard-rules.pro
new file mode 100644
index 0000000..2208f99
--- /dev/null
+++ b/stomped-lib/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Users\herte_000\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/stomped-lib/src/main/AndroidManifest.xml b/stomped-lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0349c14
--- /dev/null
+++ b/stomped-lib/src/main/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/stomped-lib/src/main/java/com/stomped/stomped/client/StompedClient.java b/stomped-lib/src/main/java/com/stomped/stomped/client/StompedClient.java
new file mode 100644
index 0000000..bab7852
--- /dev/null
+++ b/stomped-lib/src/main/java/com/stomped/stomped/client/StompedClient.java
@@ -0,0 +1,142 @@
+package com.stomped.stomped.client;
+
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.stomped.stomped.component.StompedCommand;
+import com.stomped.stomped.component.StompedFrame;
+import com.stomped.stomped.component.StompedHeaders;
+import com.stomped.stomped.connection.WebSocketConnector;
+import com.stomped.stomped.listener.StompedListener;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class StompedClient {
+
+ private final static String TAG = StompedClient.class.toString();
+
+ private ExecutorService threadPool;
+ private WebSocketConnector connector;
+ private int uniqueDestinationID = 0;
+ private StompedClientBuilder builder;
+ private boolean legacyWhitespace = true;
+ public static boolean isDebug = false;
+
+ public final static class StompedClientBuilder {
+
+ private int heartBeat = 0;
+
+ public StompedClient build(String url) {
+ return build(new WebSocketConnector.Builder().setHeartBeat(heartBeat).connect(url));
+ }
+
+ public StompedClient build(WebSocketConnector connector) {
+ return new StompedClient(connector, this);
+ }
+
+ public StompedClientBuilder setHeartBeat(int milliseconds) {
+ this.heartBeat = milliseconds;
+ return this;
+ }
+ }
+
+ private StompedClient(WebSocketConnector connector, StompedClientBuilder builder) {
+ this.connector = connector;
+ this.threadPool = Executors.newSingleThreadExecutor();
+ this.builder = builder;
+ Log.d(TAG, "StompedClient has been built.");
+ }
+
+ public void disconnect() {
+ this.connector.disconnect();
+ Log.d(TAG, "Client disconnecting");
+ }
+
+ public void send(String destination) {
+
+ //Using SEND command.
+ StompedFrame frame = StompedFrame.construct(StompedCommand.STOMP_COMMAND_SEND);
+ frame.addHeader(StompedHeaders.STOMP_HEADER_DESTINATION, destination);
+ frame.setLegacyWhitespace(legacyWhitespace);
+
+ threadPool.execute(constructOperator(frame));
+ Log.d(TAG, "Client sending to " + destination);
+ }
+
+ public void send(String destination, String body) {
+
+ StompedFrame frame = StompedFrame.construct(StompedCommand.STOMP_COMMAND_SEND, null, body);
+ frame.addHeader(StompedHeaders.STOMP_HEADER_DESTINATION, destination);
+ frame.addHeader(StompedHeaders.STOMP_HEADER_CONTENT_TYPE, "application/json");
+ frame.setLegacyWhitespace(legacyWhitespace);
+
+ threadPool.execute(constructOperator(frame));
+ Log.d(TAG, "Client sending to " + destination);
+ }
+
+ public void subscribeAndSend(String destination, String subscription, @NonNull StompedListener listener) {
+ subscribe(subscription, listener);
+ send(destination);
+ }
+
+ public void subscribe(String destination, @NonNull StompedListener listener) {
+
+ String destinationID = String.valueOf(incrementDestinationID());
+
+ listener.setDestinationID(destinationID);
+ listener.setDestination(destination);
+ StompedListenerRouter.getInstance().addListener(listener);
+
+ StompedFrame frame = StompedFrame.construct(StompedCommand.STOMP_COMMAND_SUBSCRIBE);
+ frame.addHeader(StompedHeaders.STOMP_HEADER_SUB_ID, destinationID);
+ frame.addHeader(StompedHeaders.STOMP_HEADER_DESTINATION, destination);
+ frame.addHeader(StompedHeaders.STOMP_HEADER_ACK, "auto");
+
+ threadPool.execute(constructOperator(frame));
+ Log.d(TAG, "Client subscribe to " + destination + " with destinationID " + destinationID);
+ }
+
+ public void unsubscribe(String destination) {
+
+ StompedListener currentListener = StompedListenerRouter.getInstance().removeListener(destination);
+ if (currentListener == null) {
+ Log.e(TAG, "destination: " + destination + " StompedListener is null");
+ return;
+ }
+ StompedFrame frame = StompedFrame.construct(StompedCommand.STOMP_COMMAND_UNSUBSCRIBE);
+ frame.addHeader(StompedHeaders.STOMP_HEADER_SUB_ID, currentListener.getDestinationID());
+
+ threadPool.execute(constructOperator(frame));
+ Log.d(TAG, "Client unsubscribed from " + currentListener.getDestination() + " with destinationID " + currentListener.getDestinationID());
+ }
+
+ public void setWebSocketConnector(WebSocketConnector connector) {
+ this.connector = connector;
+ }
+
+ private StompedPayloadOperator constructOperator(StompedFrame payload) {
+ return new StompedPayloadOperator(payload, connector);
+ }
+
+ private int incrementDestinationID() {
+ return uniqueDestinationID++;
+ }
+
+ /**
+ * Reverts to the old frame formatting, which included two newlines between the message body
+ * and the end-of-frame marker.
+ *
+ * Legacy: Body\n\n^@
+ *
+ * Default: Body^@
+ *
+ * @param legacyWhitespace whether to append an extra two newlines
+ * @see The STOMP spec
+ */
+ public void setLegacyWhitespace(boolean legacyWhitespace) {
+ this.legacyWhitespace = legacyWhitespace;
+ }
+}
diff --git a/stomped-lib/src/main/java/com/stomped/stomped/client/StompedListenerRouter.java b/stomped-lib/src/main/java/com/stomped/stomped/client/StompedListenerRouter.java
new file mode 100644
index 0000000..2d13dd9
--- /dev/null
+++ b/stomped-lib/src/main/java/com/stomped/stomped/client/StompedListenerRouter.java
@@ -0,0 +1,52 @@
+package com.stomped.stomped.client;
+
+/*
+ * Description: This class will route incoming messages to
+ * the correct listeners. This way, a client who is subscribed
+ * to two different paths wont have to worry about the listeners
+ * catching messages from both subscriptions.
+ */
+
+import com.stomped.stomped.component.StompedFrame;
+import com.stomped.stomped.component.StompedHeaders;
+import com.stomped.stomped.listener.StompedListener;
+
+import java.util.HashMap;
+
+public class StompedListenerRouter {
+
+ private static final String TAG = "StompedListenerRouter";
+ private static StompedListenerRouter router;
+
+ private HashMap routingTable;
+
+ private StompedListenerRouter() {
+ routingTable = new HashMap<>();
+ }
+
+ public static synchronized StompedListenerRouter getInstance() {
+
+ if (router == null) {
+
+ router = new StompedListenerRouter();
+ }
+
+ return router;
+ }
+
+ public void addListener(StompedListener listener) {
+ routingTable.put(listener.getDestination(), listener);
+ }
+
+ public StompedListener removeListener(String key) {
+ return routingTable.remove(key);
+ }
+
+ public void sendMessage(StompedFrame frame) {
+ StompedListener currentListener = routingTable.get(frame.getHeaderValueFromKey(StompedHeaders.STOMP_HEADER_DESTINATION));
+
+ if (currentListener != null) {
+ currentListener.onNotify(frame);
+ }
+ }
+}
diff --git a/stomped-lib/src/main/java/com/stomped/stomped/client/StompedPayloadOperator.java b/stomped-lib/src/main/java/com/stomped/stomped/client/StompedPayloadOperator.java
new file mode 100644
index 0000000..19006f5
--- /dev/null
+++ b/stomped-lib/src/main/java/com/stomped/stomped/client/StompedPayloadOperator.java
@@ -0,0 +1,51 @@
+package com.stomped.stomped.client;
+
+import com.stomped.stomped.component.StompedFrame;
+import com.stomped.stomped.connection.WebSocketConnector;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+class StompedPayloadOperator implements Runnable {
+
+ private StompedFrame payload;
+ private WebSocketConnector connector;
+
+ public StompedPayloadOperator(StompedFrame payload, WebSocketConnector connector) {
+ this.payload = payload;
+ this.connector = connector;
+ }
+
+ @Override
+ public void run() {
+
+ try {
+
+ synchronized (connector.getMonitor()) {
+ if (!connector.isStompConnected()) {
+ connector.getMonitor().wait();
+ }
+ }
+
+ //A write method has to exist with the payload.builds return type.
+ Method method = WebSocketConnector.class.getDeclaredMethod("write", payload.build().getClass());
+ method.invoke(connector, payload.build());
+
+ } catch (NoSuchMethodException e) {
+
+ e.printStackTrace();
+
+ } catch (InvocationTargetException e) {
+
+ e.printStackTrace();
+
+ } catch (IllegalAccessException e) {
+
+ e.printStackTrace();
+
+ } catch (InterruptedException e) {
+
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/stomped-lib/src/main/java/com/stomped/stomped/component/StompedCommand.java b/stomped-lib/src/main/java/com/stomped/stomped/component/StompedCommand.java
new file mode 100644
index 0000000..551163a
--- /dev/null
+++ b/stomped-lib/src/main/java/com/stomped/stomped/component/StompedCommand.java
@@ -0,0 +1,15 @@
+package com.stomped.stomped.component;
+
+public class StompedCommand {
+ public static final String STOMP_COMMAND_CONNECT = "CONNECT";
+ public static final String STOMP_COMMAND_CONNECTED = "CONNECTED";
+ public static final String STOMP_COMMAND_SEND = "SEND";
+ public static final String STOMP_COMMAND_SUBSCRIBE = "SUBSCRIBE";
+ public static final String STOMP_COMMAND_UNSUBSCRIBE = "UNSUBSCRIBE";
+ public static final String STOMP_COMMAND_ACK = "ACK";
+ public static final String STOMP_COMMAND_NACK = "NACK";
+ public static final String STOMP_COMMAND_BEGIN = "BEGIN";
+ public static final String STOMP_COMMAND_COMMIT = "COMMIT";
+ public static final String STOMP_COMMAND_ABORT = "ABORT";
+ public static final String STOMP_COMMAND_DISCONNECT = "DISCONNECT";
+}
diff --git a/stomped-lib/src/main/java/com/stomped/stomped/component/StompedFrame.java b/stomped-lib/src/main/java/com/stomped/stomped/component/StompedFrame.java
new file mode 100644
index 0000000..6e86bb3
--- /dev/null
+++ b/stomped-lib/src/main/java/com/stomped/stomped/component/StompedFrame.java
@@ -0,0 +1,114 @@
+package com.stomped.stomped.component;
+
+public class StompedFrame {
+
+ //TODO Change implementation of body.
+
+ private final static String TAG = "StompedFrame";
+ private String command;
+ private StompedHeaders headers;
+ private String body;
+ private String termination = "\u0000";
+ private boolean legacyWhitespace = true;
+
+ private StompedFrame() {
+ }
+
+ public static StompedFrame construct(String command) {
+ return construct(command, null);
+ }
+
+ public static StompedFrame construct(String command, StompedHeaders headers) {
+ return construct(command, headers, null);
+ }
+
+ public static StompedFrame construct(String command, StompedHeaders headers, String body) {
+
+ StompedFrame frame = new StompedFrame();
+ frame.setCommand(command);
+ frame.setStompedHeaders(headers);
+ frame.setStompedBody(body);
+
+ return frame;
+ }
+
+ public String build() {
+
+ StringBuilder builder = new StringBuilder();
+ builder.append(command);
+ builder.append("\n");
+
+ if (headers != null) {
+ builder.append(headers.getStringFormat());
+ }
+
+ builder.append("\n");
+
+ if (body != null) {
+ builder.append(body);
+ if (legacyWhitespace) {
+ builder.append("\n\n");
+ }
+ }
+ builder.append(termination);
+ return builder.toString();
+ }
+
+ public StompedHeaders createHeaders() {
+ headers = new StompedHeaders();
+ return headers;
+ }
+
+ public void addHeader(String key, String value) {
+ if (headers != null) {
+
+ headers.addHeader(key, value);
+ } else {
+
+ this.createHeaders().addHeader(key, value);
+ }
+ }
+
+ public String getHeaderValueFromKey(String key) {
+ return headers.getHeaderValueFromKey(key);
+ }
+
+ private void setCommand(String command) {
+ this.command = command;
+ }
+
+ private void setStompedHeaders(StompedHeaders headers) {
+ this.headers = headers;
+ }
+
+ private void setStompedBody(String body) {
+ this.body = body;
+ }
+
+ public String getCommand() {
+ return this.command;
+ }
+
+ public StompedHeaders getStompedHeaders() {
+ return this.headers;
+ }
+
+ public String getStompedBody() {
+ return this.body;
+ }
+
+ /**
+ * Reverts to the old frame formatting, which included two newlines between the message body
+ * and the end-of-frame marker.
+ *
+ * Legacy: Body\n\n^@
+ *
+ * Default: Body^@
+ *
+ * @param legacyWhitespace whether to append an extra two newlines
+ * @see The STOMP spec
+ */
+ public void setLegacyWhitespace(boolean legacyWhitespace) {
+ this.legacyWhitespace = legacyWhitespace;
+ }
+}
diff --git a/stomped-lib/src/main/java/com/stomped/stomped/component/StompedHeaders.java b/stomped-lib/src/main/java/com/stomped/stomped/component/StompedHeaders.java
new file mode 100644
index 0000000..a21a1a6
--- /dev/null
+++ b/stomped-lib/src/main/java/com/stomped/stomped/component/StompedHeaders.java
@@ -0,0 +1,62 @@
+package com.stomped.stomped.component;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+public class StompedHeaders {
+
+ public final static String TAG = "StompedHeaders";
+ public final static String STOMP_HEADER_DESTINATION = "destination";
+ public final static String STOMP_HEADER_CONTENT_TYPE = "content-type";
+ public final static String STOMP_HEADER_SUB_ID = "id";
+ public final static String STOMP_HEADER_ACK = "ack";
+ public final static String STOMP_HEADER_HEARTBEAT = "heart-beat";
+ public final static String STOMP_HEADER_ACCEPT_VERSION = "accept-version";
+ public final static String STOMP_HEADER_HOST = "host";
+ public final static String STOMP_HEADER_RECEIPT = "receipt";
+
+ private TreeMap stompedHeaders;
+
+ private static final String HEADER_SEPARATOR = ":";
+
+ public StompedHeaders() {
+ this.stompedHeaders = new TreeMap<>();
+ }
+
+ public StompedHeaders(TreeMap stompedHeaders) {
+ this.stompedHeaders = stompedHeaders;
+ }
+
+ public void addHeader(String key, String value) {
+ stompedHeaders.put(key, value);
+ }
+
+ public TreeMap getStompedHeaders() {
+ return stompedHeaders;
+ }
+
+ public String getStringFormat() {
+
+ //Returns a string format of the Stomp Headers.
+
+ StringBuilder builder = new StringBuilder();
+
+ for (Map.Entry entry : stompedHeaders.entrySet()) {
+
+ builder.append(entry.getKey());
+ builder.append(HEADER_SEPARATOR);
+ builder.append(entry.getValue());
+ builder.append("\n");
+ }
+
+ return builder.toString();
+ }
+
+ public String getHeaderValueFromKey(String key) {
+ return stompedHeaders.get(key);
+ }
+
+ public boolean hasHeader(String header) {
+ return (stompedHeaders.get(header) != null);
+ }
+}
diff --git a/stomped-lib/src/main/java/com/stomped/stomped/component/StompedMessageParser.java b/stomped-lib/src/main/java/com/stomped/stomped/component/StompedMessageParser.java
new file mode 100644
index 0000000..ca190c5
--- /dev/null
+++ b/stomped-lib/src/main/java/com/stomped/stomped/component/StompedMessageParser.java
@@ -0,0 +1,73 @@
+package com.stomped.stomped.component;
+
+
+import androidx.annotation.Nullable;
+
+import java.io.StringReader;
+import java.util.Scanner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class StompedMessageParser {
+
+ private final static String TAG = "StompedMessageParser";
+
+ private StompedMessageParser() {
+ }
+
+ public static StompedFrame constructFrame(String message) {
+
+ int currentPosition = 1;
+ String command;
+ StompedHeaders headers = new StompedHeaders();
+ StringBuilder body = new StringBuilder();
+
+ String[] splitMessage = message.split("\n");
+
+ command = splitMessage[0];
+
+ for (int i = currentPosition; i < splitMessage.length; i++) {
+ if (splitMessage[i].equals("")) {
+ currentPosition = i;
+ break;
+ } else {
+ String[] header = splitMessage[i].split(":");
+ headers.addHeader(header[0], header[1]);
+ }
+ }
+
+ for (int i = currentPosition; i < splitMessage.length; i++) {
+ body.append(splitMessage[i]);
+ }
+
+ return StompedFrame.construct(command, headers, body.toString());
+ }
+
+ public static final String TERMINATE_MESSAGE_SYMBOL = "\u0000";
+
+ private static final Pattern PATTERN_HEADER = Pattern.compile("([^:\\s]+)\\s*:\\s*([^:\\s]+)");
+
+ public static StompedFrame constructFrame2(@Nullable String data) {
+ if (data == null || data.trim().isEmpty()) {
+ return StompedFrame.construct("UNKNOWN");
+ }
+ Scanner reader = new Scanner(new StringReader(data));
+ reader.useDelimiter("\\n");
+ String command = reader.next();
+ StompedHeaders headers = new StompedHeaders();
+
+ while (reader.hasNext(PATTERN_HEADER)) {
+ Matcher matcher = PATTERN_HEADER.matcher(reader.next());
+ matcher.find();
+ headers.addHeader(matcher.group(1), matcher.group(2));
+ }
+
+ reader.skip("\\s");
+
+ reader.useDelimiter(TERMINATE_MESSAGE_SYMBOL);
+ String payload = reader.hasNext() ? reader.next() : null;
+
+ return StompedFrame.construct(command, headers, payload);
+ }
+
+}
diff --git a/stomped-lib/src/main/java/com/stomped/stomped/connection/WebSocketConnector.java b/stomped-lib/src/main/java/com/stomped/stomped/connection/WebSocketConnector.java
new file mode 100644
index 0000000..11de149
--- /dev/null
+++ b/stomped-lib/src/main/java/com/stomped/stomped/connection/WebSocketConnector.java
@@ -0,0 +1,158 @@
+package com.stomped.stomped.connection;
+
+import android.util.Log;
+
+import com.stomped.stomped.client.StompedClient;
+import com.stomped.stomped.client.StompedListenerRouter;
+import com.stomped.stomped.component.StompedCommand;
+import com.stomped.stomped.component.StompedFrame;
+import com.stomped.stomped.component.StompedHeaders;
+import com.stomped.stomped.component.StompedMessageParser;
+
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.WebSocket;
+import okhttp3.WebSocketListener;
+import okio.ByteString;
+
+/*
+ * Description: Currently Stomped client is built over
+ * OkHttp WebSockets only. There are no ways to implement
+ * a different connection. //TODO Change this.
+ */
+
+public class WebSocketConnector {
+
+ private final static String TAG = WebSocketConnector.class.toString();
+
+ private final Object monitor = new Object();
+ private static WebSocket stompedWebSocket;
+ private Builder builder;
+ private boolean connected;
+ private boolean stompConnected;
+
+ private class OkWebSocketListener extends WebSocketListener {
+
+ @Override
+ public void onOpen(WebSocket webSocket, Response response) {
+
+ connected = true;
+ stompedWebSocket = webSocket;
+
+ StompedFrame frame = StompedFrame.construct(StompedCommand.STOMP_COMMAND_CONNECT);
+ frame.addHeader(StompedHeaders.STOMP_HEADER_ACCEPT_VERSION, "1.0,1.1,2.0");
+ frame.addHeader(StompedHeaders.STOMP_HEADER_HOST, "stomp.github.org");
+ frame.addHeader(StompedHeaders.STOMP_HEADER_HEARTBEAT, "0," + builder.heartBeat);
+ write(frame.build());
+
+ Log.d(TAG, "OkHttp WebSocket connection created.");
+ }
+
+ @Override
+ public void onMessage(WebSocket webSocket, String message) {
+
+ StompedFrame frame = StompedMessageParser.constructFrame2(message);
+
+ synchronized (monitor) {
+ if (frame.getCommand().equals(StompedCommand.STOMP_COMMAND_CONNECTED)) {
+ stompConnected = true;
+ monitor.notifyAll();
+ }
+ }
+
+ if (frame.getStompedHeaders().hasHeader(StompedHeaders.STOMP_HEADER_DESTINATION)) {
+ StompedListenerRouter.getInstance().sendMessage(frame);
+ }
+ if (StompedClient.isDebug) {
+ Log.d(TAG, "Message received from server");
+ }
+ }
+
+ @Override
+ public void onMessage(WebSocket webSocket, ByteString bytes) {
+ if (StompedClient.isDebug) {
+ Log.d(TAG, "ByteString message received");
+ }
+ }
+
+ @Override
+ public void onClosing(WebSocket webSocket, int code, String reason) {
+ if (StompedClient.isDebug) {
+ Log.d(TAG, "WebSocket is closing: " + reason);
+ }
+ }
+
+ @Override
+ public void onFailure(WebSocket webSocket, Throwable t, Response response) {
+ Log.d(TAG, "WebSocket failure\n", t);
+ }
+ }
+
+ public final static class Builder {
+
+ private int heartBeat = 0;
+
+ public WebSocketConnector connect(String url) {
+
+ WebSocketConnector connector = new WebSocketConnector(this);
+ connector.build(url);
+
+ return connector;
+ }
+
+ public Builder setHeartBeat(int milliseconds) {
+ this.heartBeat = milliseconds;
+ return this;
+ }
+ }
+
+ private WebSocketConnector(Builder builder) {
+ this.connected = false;
+ this.stompConnected = false;
+ this.builder = builder;
+ }
+
+ private WebSocketConnector build(String url) {
+
+ //Build the client
+ OkHttpClient okClient = new OkHttpClient.Builder()
+ .readTimeout(0, TimeUnit.MILLISECONDS)
+ .build();
+
+ //Build The Request
+ Request request = new Request.Builder()
+ .url(url)
+ .build();
+
+ okClient.newWebSocket(request, new OkWebSocketListener());
+
+ return this;
+ }
+
+ public void disconnect() {
+ stompedWebSocket.close(1000, null);
+ }
+
+ public void write(String payload) {
+ stompedWebSocket.send(payload);
+ }
+
+ public void write(ByteString payload) {
+ stompedWebSocket.send(payload);
+ }
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ public boolean isStompConnected() {
+ return stompConnected;
+ }
+
+ public Object getMonitor() {
+ return monitor;
+ }
+}
diff --git a/stomped-lib/src/main/java/com/stomped/stomped/listener/StompedListener.java b/stomped-lib/src/main/java/com/stomped/stomped/listener/StompedListener.java
new file mode 100644
index 0000000..7e49d67
--- /dev/null
+++ b/stomped-lib/src/main/java/com/stomped/stomped/listener/StompedListener.java
@@ -0,0 +1,28 @@
+package com.stomped.stomped.listener;
+
+import com.stomped.stomped.component.StompedFrame;
+
+public abstract class StompedListener {
+
+ private final static String TAG = "StompedListener";
+ private String destination;
+ private String destinationID;
+
+ public abstract void onNotify(final StompedFrame frame);
+
+ public void setDestination(String destination) {
+ this.destination = destination;
+ }
+
+ public String getDestination() {
+ return destination;
+ }
+
+ public void setDestinationID(String destinationID) {
+ this.destinationID = destinationID;
+ }
+
+ public String getDestinationID() {
+ return destinationID;
+ }
+}
diff --git a/stomped-lib/src/main/res/values/strings.xml b/stomped-lib/src/main/res/values/strings.xml
new file mode 100644
index 0000000..167f7bf
--- /dev/null
+++ b/stomped-lib/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ stomped
+
diff --git a/stomped-lib/src/test/java/com/stomped/stomped/ExampleUnitTest.java b/stomped-lib/src/test/java/com/stomped/stomped/ExampleUnitTest.java
new file mode 100644
index 0000000..b84da18
--- /dev/null
+++ b/stomped-lib/src/test/java/com/stomped/stomped/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.stomped.stomped;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file