Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,18 @@ android {

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.squareup.okhttp3:okhttp:4.2.1'
implementation 'io.reactivex.rxjava2:rxkotlin:2.2.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'com.google.android.material:material:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
Expand Down
13 changes: 10 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="itis.ru.wschat" >
package="itis.ru.wschat">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity" >
android:theme="@style/AppTheme">
<activity
android:name="itis.ru.wschat.ui.login.LoginActivity"
android:label="@string/title_activity_login"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.chat.ChatActivity">
</activity>
</application>

</manifest>
9 changes: 9 additions & 0 deletions app/src/main/java/itis/ru/wschat/Const.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package itis.ru.wschat

object Const {
const val API_CHAT: String = "wss://backend-chat.cloud.technokratos.com/chat"
const val API_BASE: String = "https://backend-chat.cloud.technokratos.com/"
const val PREFERENCES: String = "SHARED_PREFERENCES"
const val DEVICE_ID: String = "device_id"
const val USERNAME: String = "username"
}
12 changes: 0 additions & 12 deletions app/src/main/java/itis/ru/wschat/MainActivity.kt

This file was deleted.

13 changes: 13 additions & 0 deletions app/src/main/java/itis/ru/wschat/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package itis.ru.wschat

import java.util.*

object Utils {
fun generateRandomString(length: Int): String? {
val AB = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
val rnd = Random()
val sb = StringBuilder(length)
for (i in 0 until length) sb.append(AB[rnd.nextInt(AB.length)])
return sb.toString()
}
}
89 changes: 89 additions & 0 deletions app/src/main/java/itis/ru/wschat/adapters/MessageAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package itis.ru.wschat.adapters

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import itis.ru.wschat.R
import itis.ru.wschat.models.Message

class MessageAdapter internal constructor(
private val uidFrom: String, private var data: MutableList<Message>
) : RecyclerView.Adapter<MessageAdapter.MessageViewHolder>() {
private var view: View? = null

override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
holder.setMessage(data[position])
}

override fun getItemCount(): Int {
return data.size
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {
return if (viewType == R.layout.item_message_to) {
view =
LayoutInflater.from(parent.context).inflate(R.layout.item_message_to, parent, false)
MessageViewHolder()
} else {
view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_message_from, parent, false)
MessageViewHolder()
}
}

override fun getItemViewType(position: Int): Int {
return if (uidFrom != data[position].user) {
R.layout.item_message_to
} else {
R.layout.item_message_from
}
}

override fun getItemId(position: Int): Long {
return data[position].id
}

fun updateData(list: MutableList<Message>) {
val diffResult = DiffUtil.calculateDiff(RecentDiffUtilCallback(this.data, list))
diffResult.dispatchUpdatesTo(this)
this.data = list
}

inner class MessageViewHolder : RecyclerView.ViewHolder(view!!) {
internal fun setMessage(message: Message) {
val textView = view?.findViewById<TextView>(R.id.text_view)
textView?.text = message.message
}
}

class RecentDiffUtilCallback internal constructor(
private val oldList: List<Message>,
private val newList: List<Message>
) : DiffUtil.Callback() {

override fun getOldListSize(): Int {
return oldList.size
}

override fun getNewListSize(): Int {
return newList.size
}

override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val old = oldList[oldItemPosition]
val new = newList[newItemPosition]
return old.id == new.id
}

override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val old = oldList[oldItemPosition]
val new = newList[newItemPosition]
return old == new
}
}


}
13 changes: 13 additions & 0 deletions app/src/main/java/itis/ru/wschat/api/LoginApiService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package itis.ru.wschat.api

import io.reactivex.Single
import itis.ru.wschat.Const
import itis.ru.wschat.models.LoginResponse
import itis.ru.wschat.models.User
import retrofit2.http.Body
import retrofit2.http.POST

interface LoginApiService{
@POST("login")
fun login(@Body user: User): Single<LoginResponse>
}
71 changes: 71 additions & 0 deletions app/src/main/java/itis/ru/wschat/api/SocketManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package itis.ru.wschat.api

import android.content.Context
import android.util.Log
import itis.ru.wschat.Const
import okhttp3.*
import java.util.concurrent.TimeUnit

private const val NORMAL_CLOSURE_STATUS = 1000

class SocketManager(
private val context: Context,
private var messageCallback: (String?, Throwable?) -> Unit,
private var getMessagesCallback: (String?, Throwable?) -> Unit
) {
private val client: OkHttpClient = OkHttpClient().newBuilder().build()
private lateinit var socket: WebSocket
private var deviceIdSent = false

fun initSocketManager() {
val request: Request = Request.Builder().url(Const.API_CHAT).build()
val listener = object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
super.onOpen(webSocket, response)
Log.d("Socket", "connect")
if (!deviceIdSent) {
}
val deviceId =
context.getSharedPreferences(Const.PREFERENCES, Context.MODE_PRIVATE)
.getString(Const.DEVICE_ID, "")
val json = "{ \"device_id\":\"$deviceId\" }"
webSocket.send(json)
deviceIdSent = true
}

override fun onMessage(webSocket: WebSocket, text: String) {
super.onMessage(webSocket, text)
if (text == "{\"status\": \"ok\"}"){
messageCallback(text, null)
}else if (text.contains("items")){
getMessagesCallback(text, null)
}
Log.d("Socket", "onMessage $text")
}

override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
Log.d("Socket", "onClosing $reason")
webSocket.close(NORMAL_CLOSURE_STATUS, null)
}

override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
super.onFailure(webSocket, t, response)
socket = client.newWebSocket(request, this)
messageCallback(null, t)
Log.d("Socket", "error $t response $response")
}

}
socket = client.newWebSocket(request, listener)
}

fun getMessages(count: Int) {
val json = "{ \"history\": { \"limit\": $count} }"
socket.send(json)
}

fun sendMessage(message: String) {
val json = "{ \"message\":\"$message\" }"
socket.send(json)
}
}
70 changes: 70 additions & 0 deletions app/src/main/java/itis/ru/wschat/models/GetMessagesResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package itis.ru.wschat.models;

import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class GetMessagesResponse {

@SerializedName("items")
@Expose
private List<Message> items = null;
@SerializedName("first")
@Expose
private Integer first;

public List<Message> getItems() {
return items;
}

public void setItems(List<Message> items) {
this.items = items;
}

public Integer getFirst() {
return first;
}

public void setFirst(Integer first) {
this.first = first;
}

}

class Item {

@SerializedName("id")
@Expose
private Integer id;
@SerializedName("user")
@Expose
private String user;
@SerializedName("message")
@Expose
private String message;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getUser() {
return user;
}

public void setUser(String user) {
this.user = user;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

}
3 changes: 3 additions & 0 deletions app/src/main/java/itis/ru/wschat/models/LoginError.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package itis.ru.wschat.models

data class LoginError(val errors: String)
3 changes: 3 additions & 0 deletions app/src/main/java/itis/ru/wschat/models/LoginResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package itis.ru.wschat.models

data class LoginResponse(val status: String)
7 changes: 7 additions & 0 deletions app/src/main/java/itis/ru/wschat/models/Message.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package itis.ru.wschat.models

data class Message(
val id: Long,
val message: String = "",
val user: String = ""
)
11 changes: 11 additions & 0 deletions app/src/main/java/itis/ru/wschat/models/Response.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package itis.ru.wschat.models


class Response<T>(val data: T?, val error: Throwable?) {

companion object {
fun <T> success(data: T): Response<T> = Response(data, null)

fun error(error: Throwable): Response<String>? = Response(null, error)
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/itis/ru/wschat/models/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package itis.ru.wschat.models

import com.google.gson.annotations.SerializedName

data class User(val username: String,
@SerializedName("device_id")
val deviceId: String)
Loading