├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── mrntlu │ │ └── websocketguide │ │ ├── MainActivity.kt │ │ ├── service │ │ └── WebSocketListener.kt │ │ ├── ui │ │ └── MainFragment.kt │ │ └── viewmodels │ │ └── MainViewModel.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── ic_launcher_background.xml │ └── ic_send.xml │ ├── layout │ ├── activity_main.xml │ └── fragment_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── values-night │ └── themes.xml │ ├── values │ ├── colors.xml │ ├── strings.xml │ └── themes.xml │ └── xml │ ├── backup_rules.xml │ └── data_extraction_rules.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Log/OS Files 23 | *.log 24 | 25 | # Android Studio generated files and folders 26 | captures/ 27 | .externalNativeBuild/ 28 | .cxx/ 29 | *.apk 30 | output.json 31 | 32 | # IntelliJ 33 | .idea/ 34 | misc.xml 35 | deploymentTargetDropDown.xml 36 | render.experimental.xml 37 | 38 | # Keystore files 39 | *.jks 40 | *.keystore 41 | 42 | # Google Services (e.g. APIs or Firebase) 43 | google-services.json 44 | 45 | # Android Profiling 46 | *.hprof -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebSocket-Guide 2 | 3 | ### WebSockets in Android with OkHttp and ViewModel 4 | https://medium.com/@burakdev/websockets-in-android-with-okhttp-and-viewmodel-776a9eed67b5 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | 3 | /src/main/java/com/mrntlu/websocketguide/Constants.kt -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.mrntlu.websocketguide' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.mrntlu.websocketguide" 12 | minSdk 26 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation 'androidx.core:core-ktx:1.9.0' 37 | implementation 'androidx.appcompat:appcompat:1.6.0' 38 | implementation 'com.google.android.material:material:1.7.0' 39 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 40 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1' 41 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' 42 | 43 | implementation("com.squareup.okhttp3:okhttp:4.10.0") 44 | 45 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10' 46 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/mrntlu/websocketguide/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.mrntlu.websocketguide 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import com.mrntlu.websocketguide.ui.MainFragment 6 | 7 | class MainActivity : AppCompatActivity() { 8 | 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | setContentView(R.layout.activity_main) 12 | if (savedInstanceState == null) { 13 | supportFragmentManager.beginTransaction() 14 | .replace(R.id.container, MainFragment.newInstance()) 15 | .commitNow() 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/mrntlu/websocketguide/service/WebSocketListener.kt: -------------------------------------------------------------------------------- 1 | package com.mrntlu.websocketguide.service 2 | 3 | import android.util.Log 4 | import com.mrntlu.websocketguide.viewmodels.MainViewModel 5 | import okhttp3.Response 6 | import okhttp3.WebSocket 7 | import okhttp3.WebSocketListener 8 | 9 | class WebSocketListener( 10 | private val viewModel: MainViewModel 11 | ): WebSocketListener() { 12 | 13 | private val TAG = "Test" 14 | 15 | override fun onOpen(webSocket: WebSocket, response: Response) { 16 | super.onOpen(webSocket, response) 17 | viewModel.setStatus(true) 18 | webSocket.send("Android Device Connected") 19 | Log.d(TAG, "onOpen:") 20 | } 21 | 22 | override fun onMessage(webSocket: WebSocket, text: String) { 23 | super.onMessage(webSocket, text) 24 | viewModel.addMessage(Pair(false, text)) 25 | Log.d(TAG, "onMessage: $text") 26 | } 27 | 28 | override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { 29 | super.onClosing(webSocket, code, reason) 30 | Log.d(TAG, "onClosing: $code $reason") 31 | } 32 | 33 | override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { 34 | super.onClosed(webSocket, code, reason) 35 | viewModel.setStatus(false) 36 | Log.d(TAG, "onClosed: $code $reason") 37 | } 38 | 39 | override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { 40 | Log.d(TAG, "onFailure: ${t.message} $response") 41 | super.onFailure(webSocket, t, response) 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/mrntlu/websocketguide/ui/MainFragment.kt: -------------------------------------------------------------------------------- 1 | package com.mrntlu.websocketguide.ui 2 | 3 | import androidx.lifecycle.ViewModelProvider 4 | import android.os.Bundle 5 | import androidx.fragment.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.widget.Button 10 | import android.widget.EditText 11 | import android.widget.ImageButton 12 | import android.widget.TextView 13 | import com.mrntlu.websocketguide.Constants 14 | import com.mrntlu.websocketguide.R 15 | import com.mrntlu.websocketguide.service.WebSocketListener 16 | import com.mrntlu.websocketguide.viewmodels.MainViewModel 17 | import okhttp3.OkHttpClient 18 | import okhttp3.Request 19 | import okhttp3.WebSocket 20 | 21 | class MainFragment : Fragment() { 22 | 23 | companion object { 24 | fun newInstance() = MainFragment() 25 | } 26 | 27 | private lateinit var viewModel: MainViewModel 28 | 29 | private lateinit var webSocketListener: WebSocketListener 30 | private val okHttpClient = OkHttpClient() 31 | private var webSocket: WebSocket? = null 32 | 33 | override fun onCreate(savedInstanceState: Bundle?) { 34 | super.onCreate(savedInstanceState) 35 | viewModel = ViewModelProvider(this).get(MainViewModel::class.java) 36 | webSocketListener = WebSocketListener(viewModel) 37 | } 38 | 39 | override fun onCreateView( 40 | inflater: LayoutInflater, container: ViewGroup?, 41 | savedInstanceState: Bundle? 42 | ): View { 43 | return inflater.inflate(R.layout.fragment_main, container, false) 44 | } 45 | 46 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 47 | super.onViewCreated(view, savedInstanceState) 48 | 49 | val messageET = view.findViewById(R.id.messageET) 50 | val sendMessageButton = view.findViewById(R.id.sendButton) 51 | val connectButton = view.findViewById