diff --git a/.travis.yml b/.travis.yml index 22da5ee..1800eea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: java +dist: trusty +after_success: + - bash <(curl -s https://codecov.io/bash) jdk: - oraclejdk8 sudo: false diff --git a/ChatExample/.gitignore b/ChatExample/.gitignore new file mode 100644 index 0000000..2b75303 --- /dev/null +++ b/ChatExample/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/ChatExample/app/.gitignore b/ChatExample/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/ChatExample/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/ChatExample/app/build.gradle b/ChatExample/app/build.gradle new file mode 100644 index 0000000..18dd1a7 --- /dev/null +++ b/ChatExample/app/build.gradle @@ -0,0 +1,53 @@ +apply plugin: 'com.android.application' + +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 30 + defaultConfig { + applicationId "com.github.dsrees.chatexample" + minSdkVersion 19 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + targetCompatibility = "8" + sourceCompatibility = "8" + } +} + +dependencies { + /* + To update the JavaPhoenixClient, either use the latest dependency from mavenCentral() + OR run + `./gradlew jar` + and copy + `/build/lib/*.jar` to `/ChatExample/app/libs` + and comment out the mavenCentral() dependency + */ + implementation fileTree(dir: 'libs', include: ['*.jar']) +// implementation 'com.github.dsrees:JavaPhoenixClient:0.3.4' + + + implementation "com.google.code.gson:gson:2.8.5" + implementation "com.squareup.okhttp3:okhttp:3.12.2" + + + implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + +} diff --git a/ChatExample/app/libs/JavaPhoenixClient-0.7.0.jar b/ChatExample/app/libs/JavaPhoenixClient-0.7.0.jar new file mode 100644 index 0000000..6dbc437 Binary files /dev/null and b/ChatExample/app/libs/JavaPhoenixClient-0.7.0.jar differ diff --git a/ChatExample/app/proguard-rules.pro b/ChatExample/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/ChatExample/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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/ChatExample/app/src/main/AndroidManifest.xml b/ChatExample/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..15f50d6 --- /dev/null +++ b/ChatExample/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MainActivity.kt b/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MainActivity.kt new file mode 100644 index 0000000..6e225dd --- /dev/null +++ b/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MainActivity.kt @@ -0,0 +1,140 @@ +package com.github.dsrees.chatexample + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.util.Log +import android.widget.ArrayAdapter +import android.widget.Button +import android.widget.EditText +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.android.synthetic.main.activity_main.* +import org.phoenixframework.Channel +import org.phoenixframework.Socket + +class MainActivity : AppCompatActivity() { + + companion object { + const val TAG = "MainActivity" + } + + private val messagesAdapter = MessagesAdapter() + private val layoutManager = LinearLayoutManager(this) + + + // Use when connecting to https://github.com/dwyl/phoenix-chat-example + // private val socket = Socket("https://phxchat.herokuapp.com/socket/websocket") + // private val topic = "room:lobby" + + // Use when connecting to local server + private val socket = Socket("ws://10.0.2.2:4000/socket/websocket") + private val topic = "rooms:lobby" + + private var lobbyChannel: Channel? = null + + private val username: String + get() = username_input.text.toString() + + private val message: String + get() = message_input.text.toString() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + + layoutManager.stackFromEnd = true + + messages_recycler_view.layoutManager = layoutManager + messages_recycler_view.adapter = messagesAdapter + + socket.onOpen { + this.addText("Socket Opened") + runOnUiThread { connect_button.text = "Disconnect" } + } + + socket.onClose { + this.addText("Socket Closed") + runOnUiThread { connect_button.text = "Connect" } + } + + socket.onError { throwable, response -> + Log.e(TAG, "Socket Errored $response", throwable) + this.addText("Socket Error") + } + + socket.logger = { + Log.d(TAG, "SOCKET $it") + } + + + connect_button.setOnClickListener { + if (socket.isConnected) { + this.disconnectAndLeave() + } else { + this.disconnectAndLeave() + this.connectAndJoin() + } + } + + send_button.setOnClickListener { sendMessage() } + } + + private fun sendMessage() { + val payload = mapOf("user" to username, "body" to message) + this.lobbyChannel?.push("new:msg", payload) + ?.receive("ok") { Log.d(TAG, "success $it") } + ?.receive("error") { Log.d(TAG, "error $it") } + + message_input.text.clear() + } + + private fun disconnectAndLeave() { + // Be sure the leave the channel or call socket.remove(lobbyChannel) + lobbyChannel?.leave() + socket.disconnect { this.addText("Socket Disconnected") } + } + + private fun connectAndJoin() { + val channel = socket.channel(topic, mapOf("status" to "joining")) + channel.on("join") { + this.addText("You joined the room") + } + + channel.on("new:msg") { message -> + val payload = message.payload + val username = payload["user"] as? String + val body = payload["body"] + + + if (username != null && body != null) { + this.addText("[$username] $body") + } + } + + channel.on("user:entered") { + this.addText("[anonymous entered]") + } + + this.lobbyChannel = channel + channel + .join() + .receive("ok") { + this.addText("Joined Channel") + } + .receive("error") { + this.addText("Failed to join channel: ${it.payload}") + } + + + this.socket.connect() + } + + private fun addText(message: String) { + runOnUiThread { + this.messagesAdapter.add(message) + layoutManager.smoothScrollToPosition(messages_recycler_view, null, messagesAdapter.itemCount) + } + + } + +} diff --git a/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MessagesAdapter.kt b/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MessagesAdapter.kt new file mode 100644 index 0000000..e99b294 --- /dev/null +++ b/ChatExample/app/src/main/java/com/github/dsrees/chatexample/MessagesAdapter.kt @@ -0,0 +1,34 @@ +package com.github.dsrees.chatexample + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView + +class MessagesAdapter : RecyclerView.Adapter() { + + private var messages: MutableList = mutableListOf() + + fun add(message: String) { + messages.add(message) + notifyItemInserted(messages.size) + } + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_message, parent, false) + return ViewHolder(view) + } + + override fun getItemCount(): Int = messages.size + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.label.text = messages[position] + } + + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val label: TextView = itemView.findViewById(R.id.item_message_label) + } + +} \ No newline at end of file diff --git a/ChatExample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/ChatExample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..6348baa --- /dev/null +++ b/ChatExample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/ChatExample/app/src/main/res/drawable/ic_launcher_background.xml b/ChatExample/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..a0ad202 --- /dev/null +++ b/ChatExample/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChatExample/app/src/main/res/layout/activity_main.xml b/ChatExample/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..bc78d34 --- /dev/null +++ b/ChatExample/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,65 @@ + + + +