├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── mayurrokade
│ │ └── chatapp
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── com
│ │ │ └── mayurrokade
│ │ │ └── chatapp
│ │ │ ├── BaseApplication.java
│ │ │ ├── BasePresenter.java
│ │ │ ├── BaseView.java
│ │ │ ├── about
│ │ │ └── AboutActivity.java
│ │ │ ├── chat
│ │ │ ├── ChatActivity.java
│ │ │ ├── ChatContract.java
│ │ │ ├── ChatMessagesAdapter.java
│ │ │ └── ChatPresenter.java
│ │ │ ├── data
│ │ │ ├── ChatMessage.java
│ │ │ └── source
│ │ │ │ ├── DataSource.java
│ │ │ │ ├── Repository.java
│ │ │ │ ├── local
│ │ │ │ └── LocalDataSource.java
│ │ │ │ └── remote
│ │ │ │ └── RemoteDataSource.java
│ │ │ ├── eventservice
│ │ │ ├── EventListener.java
│ │ │ ├── EventService.java
│ │ │ └── EventServiceImpl.java
│ │ │ └── util
│ │ │ ├── AppLifeCycleObserver.java
│ │ │ ├── Injection.java
│ │ │ ├── TextUtils.java
│ │ │ ├── User.java
│ │ │ └── schedulers
│ │ │ ├── BaseSchedulerProvider.java
│ │ │ └── SchedulerProvider.java
│ └── res
│ │ ├── drawable-hdpi
│ │ ├── ic_account.png
│ │ ├── ic_back.png
│ │ ├── ic_edit.png
│ │ ├── ic_info.png
│ │ ├── ic_logo_no_background.png
│ │ └── ic_send.png
│ │ ├── drawable-mdpi
│ │ ├── ic_account.png
│ │ ├── ic_back.png
│ │ ├── ic_edit.png
│ │ ├── ic_info.png
│ │ ├── ic_logo_no_background.png
│ │ └── ic_send.png
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable-xhdpi
│ │ ├── ic_account.png
│ │ ├── ic_back.png
│ │ ├── ic_edit.png
│ │ ├── ic_info.png
│ │ ├── ic_logo_no_background.png
│ │ └── ic_send.png
│ │ ├── drawable-xxhdpi
│ │ ├── ic_account.png
│ │ ├── ic_back.png
│ │ ├── ic_edit.png
│ │ ├── ic_info.png
│ │ ├── ic_logo_no_background.png
│ │ └── ic_send.png
│ │ ├── drawable-xxxhdpi
│ │ ├── about_icon_copy_right.xml
│ │ └── ic_logo_no_background.png
│ │ ├── drawable
│ │ ├── bg_received_message.xml
│ │ ├── bg_rounded_border.xml
│ │ ├── bg_rounded_filled.xml
│ │ ├── bg_sent_message.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_linkedin_logo.xml
│ │ ├── ic_medium_logo.xml
│ │ └── profile_image_round.png
│ │ ├── layout
│ │ ├── activity_about.xml
│ │ ├── activity_chat.xml
│ │ ├── dialog_set_username.xml
│ │ ├── item_message_received.xml
│ │ └── item_message_sent.xml
│ │ ├── menu
│ │ └── menu_options.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── mayurrokade
│ └── chatapp
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── Android MVP_realtime.png
├── demo.gif
├── logo_socket_chat_circle.png
├── realtime_android_architecture.png
├── socket_chat.png
└── socket_chat_backdrop.png
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea/*
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Mayur Rokade
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Socket Chat
6 |
A demo app to showcase Android MVP Realtime architecture.
7 |
8 | Socket Chat is a demo app built using Android MVP Realtime architecture. You can read more about the architecture on my Medium article https://bit.ly/2KXcTan .
9 |
10 | This app connects to Socket.IO chat room at https://socket-io-chat.now.sh. Suggestions for further improvements are most welcome.
11 |
12 | ## Screenshot
13 |
14 |
15 |
16 |
17 | ## Problem Statement
18 | Most of us Android developers have created apps using the MVP architecture. A regular android app involves making HTTP API calls to the server, getting some data and rendering the view. Using these APIs, you can accomplish things like
19 |
20 | - Booking a cab
21 | - Ordering lunch
22 | - Transferring money etc.
23 |
24 | As you can see, HTTP APIs are quite simple, scalable and you can get a lot of things done. Once you learn MVP with RxJava and Retrofit you are ready to take on the world with your amazing app. Yay!!
25 |
26 | This feeling of awesomeness is quickly replaced by confusion the moment you try to build realtime apps like
27 |
28 | - Chat app
29 | - Multiplayer gaming app
30 | - Realtime stock price updates app etc.
31 |
32 | ## Limitations of Android MVP
33 | When I say Android MVP, I am referring to the [Todo-MVP-RxJava example from googlesamples](https://github.com/googlesamples/android-architecture/tree/todo-mvp-rxjava/).
34 |
35 | In this Android MVP example,
36 |
37 | - View is responsible for rendering the UI
38 | - Presenter is responsible for presenting the data
39 | - Repository is responsible for getting the data
40 |
41 | This architecuture is based on request-response model, where the client requests for some resource from the server and the server sends back a response. But the other way round, where the server sends data to the client and the client handles the data, is not possible in the architecture. Simply because, the client is not actively listening for incoming data from the server.
42 |
43 | ## Proposed Solution
44 | So for the app to be realtime app, it should be able to send and receive events/data from the server. Based on this, if we try to draw an architecture, it would look some thing like this.
45 |
46 |
47 |
48 |
49 |
50 | This Android project is an working implementation of the proposed Android MVP Realtime architecture.
51 |
52 | ## Usage
53 | If you want to try out the app, follow the below steps
54 |
55 | - Clone the repo
56 | - Open the project in Android Studio
57 | - Run it on your Android phone
58 | - In the web browser, open https://socket-io-chat.now.sh.
59 |
60 | This way you can send message between the app and the web chatroom.
61 |
62 | ## Quick Demo
63 | Check out this video on YouTube to see the app in action.
64 |
65 | [](http://www.youtube.com/watch?v=BJ70gpBXBcU "Socket Chat - Android MVP Realtime architecture")
66 |
67 | **NOTE:** Sometimes, when you join a Socket.IO chat room on web, it might be a different chat room than the one to which the app is connected. I don't know what causes this since I am not the developer of Socket.IO web chat room.
68 |
69 | ## Disclaimer
70 | This is a side project app to experiment/play around with Android framework. This is not a production app.
71 | So there is no guarantee that issues/feature-requests/enhancements will be worked upon.
72 |
73 | ## License
74 | MIT
75 |
76 | ---
77 | > Github [@mayuroks](https://github.com/mayuroks) ~ Twitter [@mayuroks](https://twitter.com/mayuroks) ~ Medium [@mayuroks](https://medium.com/@mayuroks)
78 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | *.idea/
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | defaultConfig {
6 | applicationId "com.mayurrokade.chatapp"
7 | minSdkVersion 16
8 | targetSdkVersion 27
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:27.1.1'
24 | implementation 'com.android.support:design:27.1.1'
25 | implementation 'com.android.support.constraint:constraint-layout:1.1.2'
26 | implementation 'com.android.support:recyclerview-v7:27.1.1'
27 | implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
28 | implementation 'io.reactivex.rxjava2:rxjava:2.1.7'
29 | implementation('io.socket:socket.io-client:1.0.0') {
30 | // excluding org.json which is provided by Android
31 | exclude group: 'org.json', module: 'json'
32 | }
33 | implementation 'android.arch.lifecycle:extensions:1.1.1'
34 | implementation 'com.wang.avi:library:2.1.3'
35 | implementation 'com.github.medyo:android-about-page:1.2.4'
36 | testImplementation 'junit:junit:4.12'
37 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
38 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
39 | }
40 |
--------------------------------------------------------------------------------
/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
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/mayurrokade/chatapp/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.mayurrokade.chatapp;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.mayurrokade.chatapp", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayuroks/android-mvp-realtime-chat/188f9df083f2e0ecfab2c441782913723e9a2fc1/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/com/mayurrokade/chatapp/BaseApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Mayur Rokade
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | package com.mayurrokade.chatapp;
24 |
25 | import android.app.Application;
26 | import android.arch.lifecycle.ProcessLifecycleOwner;
27 |
28 | import com.mayurrokade.chatapp.util.AppLifeCycleObserver;
29 | import com.mayurrokade.chatapp.util.User;
30 |
31 | import java.util.UUID;
32 |
33 | public class BaseApplication extends Application {
34 | @Override
35 | public void onCreate() {
36 | super.onCreate();
37 |
38 | // Observer to detect if the app is in background or foreground.
39 | AppLifeCycleObserver lifeCycleObserver
40 | = new AppLifeCycleObserver(getApplicationContext());
41 |
42 | // Adding the above observer to process lifecycle
43 | ProcessLifecycleOwner.get()
44 | .getLifecycle()
45 | .addObserver(lifeCycleObserver);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mayurrokade/chatapp/BasePresenter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Mayur Rokade
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | package com.mayurrokade.chatapp;
24 |
25 | public interface BasePresenter {
26 |
27 | void subscribe();
28 |
29 | void unsubscribe();
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mayurrokade/chatapp/BaseView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Mayur Rokade
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | package com.mayurrokade.chatapp;
24 |
25 | public interface BaseView {
26 |
27 | void initView();
28 |
29 | void setPresenter(T presenter);
30 |
31 | void showAlert(String message, boolean isError);
32 |
33 | void hideAlert();
34 |
35 | void showProgress();
36 |
37 | void hideProgress();
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mayurrokade/chatapp/about/AboutActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Mayur Rokade
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | package com.mayurrokade.chatapp.about;
24 |
25 | import android.content.Intent;
26 | import android.net.Uri;
27 | import android.os.Bundle;
28 | import android.support.v7.app.AppCompatActivity;
29 | import android.view.Gravity;
30 | import android.view.MenuItem;
31 | import android.view.View;
32 | import android.widget.Toast;
33 |
34 | import com.mayurrokade.chatapp.R;
35 |
36 | import java.util.Calendar;
37 |
38 | import mehdi.sakout.aboutpage.AboutPage;
39 | import mehdi.sakout.aboutpage.Element;
40 |
41 | public class AboutActivity extends AppCompatActivity {
42 |
43 | @Override
44 | protected void onCreate(Bundle savedInstanceState) {
45 | super.onCreate(savedInstanceState);
46 | setContentView(R.layout.activity_about);
47 |
48 | getSupportActionBar().setTitle("About Me");
49 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
50 | getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_back);
51 |
52 | View aboutPage = new AboutPage(this)
53 | .isRTL(false)
54 | .setImage(R.drawable.profile_image_round)
55 | .addItem(new Element().setTitle("Get in touch"))
56 | .addItem(new Element().setTitle("LinkedIn")
57 | .setIconDrawable(R.drawable.ic_linkedin_logo)
58 | .setIconTint(R.color.colorTextRegular)
59 | .setOnClickListener(new View.OnClickListener() {
60 | @Override
61 | public void onClick(View view) {
62 | Uri webpage = Uri.parse("https://linkedin.com/in/mayurrokade/");
63 | startActivity(new Intent(Intent.ACTION_VIEW, webpage));
64 | }
65 | }))
66 | .addItem(new Element().setTitle("Medium")
67 | .setIconDrawable(R.drawable.ic_medium_logo)
68 | .setIconTint(R.color.colorTextRegular)
69 | .setOnClickListener(new View.OnClickListener() {
70 | @Override
71 | public void onClick(View view) {
72 | Uri webpage = Uri.parse("https://medium.com/@mayuroks");
73 | startActivity(new Intent(Intent.ACTION_VIEW, webpage));
74 | }
75 | }))
76 | .addGitHub("mayuroks")
77 | .addFacebook("mayurzenith")
78 | .addInstagram("mayurzenith")
79 | .addTwitter("mayuroks")
80 | .addEmail("mayurzenith@gmail.com")
81 | .addItem(getCopyRightsElement())
82 | .create();
83 |
84 | setContentView(aboutPage);
85 | }
86 |
87 | @Override
88 | public boolean onOptionsItemSelected(MenuItem item) {
89 | if (item.getItemId() == android.R.id.home) {
90 | onBackPressed();
91 | }
92 |
93 | return super.onOptionsItemSelected(item);
94 | }
95 |
96 | Element getCopyRightsElement() {
97 | Element copyRightsElement = new Element();
98 | final String copyrights = String.format(getString(R.string.copy_right),
99 | Calendar.getInstance().get(Calendar.YEAR));
100 | copyRightsElement.setTitle(copyrights);
101 | copyRightsElement.setIconDrawable(R.drawable.about_icon_copy_right);
102 | copyRightsElement.setIconTint(mehdi.sakout.aboutpage.R.color.about_item_icon_color);
103 | copyRightsElement.setIconNightTint(android.R.color.white);
104 | copyRightsElement.setGravity(Gravity.CENTER);
105 | copyRightsElement.setOnClickListener(new View.OnClickListener() {
106 | @Override
107 | public void onClick(View v) {
108 | Toast.makeText(AboutActivity.this, copyrights, Toast.LENGTH_SHORT).show();
109 | }
110 | });
111 | return copyRightsElement;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mayurrokade/chatapp/chat/ChatActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Mayur Rokade
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | package com.mayurrokade.chatapp.chat;
24 |
25 | import android.animation.Animator;
26 | import android.animation.AnimatorListenerAdapter;
27 | import android.content.Intent;
28 | import android.os.Bundle;
29 | import android.os.Handler;
30 | import android.support.v4.content.ContextCompat;
31 | import android.support.v7.app.AlertDialog;
32 | import android.support.v7.app.AppCompatActivity;
33 | import android.support.v7.widget.LinearLayoutManager;
34 | import android.support.v7.widget.RecyclerView;
35 | import android.text.Editable;
36 | import android.text.TextWatcher;
37 | import android.util.Log;
38 | import android.view.Menu;
39 | import android.view.MenuInflater;
40 | import android.view.MenuItem;
41 | import android.view.View;
42 | import android.widget.Button;
43 | import android.widget.EditText;
44 | import android.widget.ImageView;
45 | import android.widget.LinearLayout;
46 | import android.widget.TextView;
47 |
48 | import com.mayurrokade.chatapp.R;
49 | import com.mayurrokade.chatapp.about.AboutActivity;
50 | import com.mayurrokade.chatapp.data.ChatMessage;
51 | import com.mayurrokade.chatapp.util.Injection;
52 | import com.mayurrokade.chatapp.util.TextUtils;
53 | import com.mayurrokade.chatapp.util.User;
54 |
55 | import org.json.JSONException;
56 | import org.json.JSONObject;
57 |
58 | import java.util.ArrayList;
59 |
60 | public class ChatActivity
61 | extends AppCompatActivity
62 | implements ChatContract.View {
63 |
64 | private static final String TAG = ChatActivity.class.getSimpleName();
65 | private static final long TYPING_TIMER_LENGTH = 3000;
66 | private static final long ALERT_LENGTH = 2000;
67 | private RecyclerView rvChatMessages;
68 | private RecyclerView.LayoutManager mLayoutManager;
69 | private ChatMessagesAdapter mChatMessagesAdapter;
70 | private EditText etSendMessage;
71 | private ImageView ivSendMessage;
72 | private LinearLayout llTyping;
73 | private TextView tvTyping, tvAlert;
74 | private ChatContract.Presenter mPresenter;
75 | private boolean mTyping = false;
76 | private Handler mTypingHandler = new Handler();
77 | private int mAlerterHeight;
78 |
79 | @Override
80 | protected void onCreate(Bundle savedInstanceState) {
81 | super.onCreate(savedInstanceState);
82 | setContentView(R.layout.activity_chat);
83 | }
84 |
85 | @Override
86 | protected void onResume() {
87 | super.onResume();
88 |
89 | new ChatPresenter(this, this,
90 | Injection.provideSchedulerProvider(),
91 | Injection.providesRepository(this));
92 | }
93 |
94 | @Override
95 | protected void onPause() {
96 | super.onPause();
97 | mPresenter.unsubscribe();
98 | }
99 |
100 | @Override
101 | public boolean onCreateOptionsMenu(Menu menu) {
102 | MenuInflater inflater = getMenuInflater();
103 | inflater.inflate(R.menu.menu_options, menu);
104 | return true;
105 | }
106 |
107 | @Override
108 | public boolean onOptionsItemSelected(MenuItem item) {
109 | switch (item.getItemId()) {
110 | case R.id.change_name:
111 | askUsername();
112 | break;
113 | case R.id.info:
114 | showInfo();
115 | break;
116 | default:
117 | break;
118 | }
119 |
120 | return super.onOptionsItemSelected(item);
121 | }
122 |
123 | @Override
124 | public void initView() {
125 | // Init UI elements
126 | rvChatMessages = findViewById(R.id.rvChatMessages);
127 | etSendMessage = findViewById(R.id.etSendMessage);
128 | ivSendMessage = findViewById(R.id.btnSendMessage);
129 | tvTyping = findViewById(R.id.tvTyping);
130 | llTyping = findViewById(R.id.llTyping);
131 | tvAlert = findViewById(R.id.tvAlert);
132 | tvAlert.setTranslationY(-100);
133 |
134 | getSupportActionBar().setTitle("Realtime MVP Chat");
135 |
136 | // Ask the user to set a username,
137 | // when the app opens up.
138 | if (!User.isUsernameUpdated()) {
139 | askUsername();
140 | }
141 |
142 | setupChatMessages();
143 | setupSendButton();
144 | setupTextWatcher();
145 | }
146 |
147 | @Override
148 | public void setPresenter(ChatContract.Presenter presenter) {
149 | mPresenter = presenter;
150 | }
151 |
152 | @Override
153 | public void showAlert(final String message, final boolean isError) {
154 | Log.i(TAG, "showAlert: " + message);
155 | final int successColor = ContextCompat.getColor(this, R.color.colorSuccess);
156 | final int errorColor = ContextCompat.getColor(this, R.color.colorError);
157 |
158 | runOnUiThread(new Runnable() {
159 | @Override
160 | public void run() {
161 | tvAlert.setText(message);
162 |
163 | if (isError) {
164 | tvAlert.setBackgroundColor(errorColor);
165 | } else {
166 | tvAlert.setBackgroundColor(successColor);
167 | }
168 |
169 | mAlerterHeight = tvAlert.getHeight();
170 | tvAlert.setTranslationY(-1 * mAlerterHeight);
171 |
172 | tvAlert.animate()
173 | .translationY(0)
174 | .setDuration(500)
175 | .setListener(new AnimatorListenerAdapter() {
176 | @Override
177 | public void onAnimationEnd(Animator animation) {
178 | super.onAnimationEnd(animation);
179 | final Handler handler = new Handler();
180 | handler.postDelayed(new Runnable() {
181 | @Override
182 | public void run() {
183 | hideAlert();
184 | }
185 | }, ALERT_LENGTH);
186 | }
187 | });
188 | }
189 | });
190 | }
191 |
192 | @Override
193 | public void hideAlert() {
194 | tvAlert.animate()
195 | .translationY(-1 * mAlerterHeight)
196 | .setDuration(500)
197 | .setListener(new AnimatorListenerAdapter() {
198 | @Override
199 | public void onAnimationEnd(Animator animation) {
200 | super.onAnimationEnd(animation);
201 | tvAlert.setText("");
202 | }
203 | });
204 | }
205 |
206 | @Override
207 | public void showProgress() {
208 |
209 | }
210 |
211 | @Override
212 | public void hideProgress() {
213 |
214 | }
215 |
216 | private void setupChatMessages() {
217 | mChatMessagesAdapter = new ChatMessagesAdapter(new ArrayList(), this);
218 | mLayoutManager = new LinearLayoutManager(this);
219 | rvChatMessages.setAdapter(mChatMessagesAdapter);
220 | rvChatMessages.setLayoutManager(mLayoutManager);
221 | }
222 |
223 | private void setupSendButton() {
224 | ivSendMessage.setOnClickListener(new View.OnClickListener() {
225 | @Override
226 | public void onClick(View view) {
227 | sendMessage();
228 | }
229 | });
230 | }
231 |
232 | private void sendMessage() {
233 | String message = etSendMessage.getText().toString().trim();
234 |
235 | if (TextUtils.isValidString(message)) {
236 | ChatMessage chatMessage = new ChatMessage(
237 | User.getUsername(), message, ChatMessage.TYPE_MESSAGE_SENT);
238 | mPresenter.sendMessage(chatMessage);
239 | addMessage(chatMessage);
240 | etSendMessage.setText("");
241 | }
242 | }
243 |
244 | private void addMessage(ChatMessage chatMessage) {
245 | mChatMessagesAdapter.addNewMessage(chatMessage);
246 | rvChatMessages.scrollToPosition(mChatMessagesAdapter.getItemCount() - 1);
247 | }
248 |
249 | private void askUsername() {
250 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
251 | View view = getLayoutInflater().inflate(
252 | R.layout.dialog_set_username, null);
253 | final AlertDialog dialog = builder.setView(view).setCancelable(false).create();
254 |
255 | Button btnSave = view.findViewById(R.id.btnSave);
256 | Button btnClose = view.findViewById(R.id.btnClose);
257 | final EditText etUsername = view.findViewById(R.id.etUsername);
258 | etUsername.setText(User.getUsername());
259 |
260 | btnSave.setOnClickListener(new View.OnClickListener() {
261 | @Override
262 | public void onClick(View view) {
263 | String username = etUsername.getText().toString().trim();
264 | if (TextUtils.isValidString(username)) {
265 | mPresenter.changeUsername(username);
266 | dialog.dismiss();
267 | }
268 | }
269 | });
270 |
271 | btnClose.setOnClickListener(new View.OnClickListener() {
272 | @Override
273 | public void onClick(View view) {
274 | dialog.dismiss();
275 | }
276 | });
277 |
278 | dialog.show();
279 | }
280 |
281 | private void showInfo() {
282 | startActivity(new Intent(this, AboutActivity.class));
283 | }
284 |
285 | private void setupTextWatcher() {
286 | etSendMessage.addTextChangedListener(new TextWatcher() {
287 | @Override
288 | public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
289 |
290 | }
291 |
292 | @Override
293 | public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
294 | if (!mTyping) {
295 | mTyping = true;
296 | mPresenter.onTyping();
297 | }
298 |
299 | mTypingHandler.removeCallbacks(onTypingTimeout);
300 | mTypingHandler.postDelayed(onTypingTimeout, TYPING_TIMER_LENGTH);
301 | }
302 |
303 | @Override
304 | public void afterTextChanged(Editable editable) {
305 |
306 | }
307 | });
308 | }
309 |
310 | @Override
311 | public void onConnect(final Object... args) {
312 | showAlert("Connected", false);
313 | }
314 |
315 | @Override
316 | public void onDisconnect(final Object... args) {
317 | showAlert("Disconnected", false);
318 | }
319 |
320 | @Override
321 | public void onConnectError(final Object... args) {
322 | showAlert("No internet connection", true);
323 | }
324 |
325 | @Override
326 | public void onConnectTimeout(final Object... args) {
327 | showAlert("Connection Timeout", false);
328 |
329 | }
330 |
331 | @Override
332 | public void onNewMessage(final Object... args) {
333 | runOnUiThread(new Runnable() {
334 | @Override
335 | public void run() {
336 | JSONObject data = (JSONObject) args[0];
337 | String username;
338 | String message;
339 | try {
340 | username = data.getString("username");
341 | message = data.getString("message");
342 | ChatMessage chatMessage = new ChatMessage(
343 | username, message, ChatMessage.TYPE_MESSAGE_RECEIVED);
344 | addMessage(chatMessage);
345 | } catch (JSONException e) {
346 | Log.e(TAG, e.getMessage());
347 | return;
348 | }
349 | }
350 | });
351 | }
352 |
353 | @Override
354 | public void onUserJoined(Object... args) {
355 | JSONObject data = (JSONObject) args[0];
356 | String username;
357 | int numUsers;
358 | try {
359 | username = data.getString("username");
360 | numUsers = data.getInt("numUsers");
361 | } catch (JSONException e) {
362 | Log.e(TAG, e.getMessage());
363 | return;
364 | }
365 |
366 | showAlert(username + " has joined", false);
367 | }
368 |
369 | @Override
370 | public void onUserLeft(Object... args) {
371 | JSONObject data = (JSONObject) args[0];
372 | String username;
373 | int numUsers;
374 | try {
375 | username = data.getString("username");
376 | numUsers = data.getInt("numUsers");
377 | } catch (JSONException e) {
378 | Log.e(TAG, e.getMessage());
379 | return;
380 | }
381 |
382 | showAlert(username + " has left", false);
383 | }
384 |
385 | @Override
386 | public void onTyping(Object... args) {
387 | JSONObject data = (JSONObject) args[0];
388 | String username;
389 | try {
390 | username = data.getString("username");
391 | } catch (JSONException e) {
392 | Log.e(TAG, e.getMessage());
393 | return;
394 | }
395 |
396 | addTyping(username);
397 | Log.i(TAG, "onTyping: " + username);
398 | }
399 |
400 | @Override
401 | public void onStopTyping(Object... args) {
402 | JSONObject data = (JSONObject) args[0];
403 | String username;
404 | try {
405 | username = data.getString("username");
406 | } catch (JSONException e) {
407 | Log.e(TAG, e.getMessage());
408 | return;
409 | }
410 | removeTyping(username);
411 | }
412 |
413 | @Override
414 | public void onMessageDelivered(ChatMessage chatMessage) {
415 | // Update UI to show the message has been delivered
416 | }
417 |
418 | @Override
419 | public void updateUsername(String username) {
420 | User.setUsername(username);
421 | }
422 |
423 | private Runnable onTypingTimeout = new Runnable() {
424 | @Override
425 | public void run() {
426 | if (!mTyping) return;
427 |
428 | mTyping = false;
429 | mPresenter.onStopTyping();
430 | }
431 | };
432 |
433 | private void addTyping(final String username) {
434 | runOnUiThread(new Runnable() {
435 | @Override
436 | public void run() {
437 | tvTyping.setText(username + " is typing");
438 | llTyping.setVisibility(View.VISIBLE);
439 | }
440 | });
441 | }
442 |
443 | private void removeTyping(String username) {
444 | runOnUiThread(new Runnable() {
445 | @Override
446 | public void run() {
447 | tvTyping.setText("");
448 | llTyping.setVisibility(View.GONE);
449 | }
450 | });
451 | }
452 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/mayurrokade/chatapp/chat/ChatContract.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Mayur Rokade
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | package com.mayurrokade.chatapp.chat;
24 |
25 | import com.mayurrokade.chatapp.BasePresenter;
26 | import com.mayurrokade.chatapp.BaseView;
27 | import com.mayurrokade.chatapp.data.ChatMessage;
28 | import com.mayurrokade.chatapp.eventservice.EventListener;
29 |
30 | /**
31 | * This is a contract between chat view and chat presenter.
32 | *
33 | */
34 | public interface ChatContract {
35 |
36 | interface View extends BaseView, EventListener {
37 |
38 | void onMessageDelivered(ChatMessage chatMessage);
39 |
40 | void updateUsername(String username);
41 | }
42 |
43 | interface Presenter extends BasePresenter, EventListener {
44 |
45 | void sendMessage(ChatMessage chatMessage);
46 |
47 | void changeUsername(String username);
48 |
49 | void onTyping();
50 |
51 | void onStopTyping();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mayurrokade/chatapp/chat/ChatMessagesAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Mayur Rokade
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | package com.mayurrokade.chatapp.chat;
24 |
25 | import android.content.Context;
26 | import android.support.annotation.NonNull;
27 | import android.support.v7.widget.RecyclerView;
28 | import android.view.LayoutInflater;
29 | import android.view.View;
30 | import android.view.ViewGroup;
31 | import android.widget.TextView;
32 |
33 | import com.mayurrokade.chatapp.R;
34 | import com.mayurrokade.chatapp.data.ChatMessage;
35 |
36 | import java.util.List;
37 |
38 | /**
39 | * ChatMessages adapter.
40 | *
41 | */
42 | public class ChatMessagesAdapter extends RecyclerView.Adapter {
43 |
44 | private List mItems;
45 | private Context mContext;
46 |
47 | /**
48 | * Constructor to create a new ChatMessagesAdapter
49 | *
50 | * @param items
51 | * @param context
52 | */
53 | public ChatMessagesAdapter(List items, Context context) {
54 | mItems = items;
55 | mContext = context;
56 | }
57 |
58 | @NonNull
59 | @Override
60 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
61 | RecyclerView.ViewHolder viewHolder;
62 | View view;
63 |
64 | if (viewType == ChatMessage.TYPE_MESSAGE_RECEIVED) {
65 | view = LayoutInflater.from(mContext)
66 | .inflate(R.layout.item_message_received, parent, false);
67 | viewHolder = new ReceivedMessageViewHolder(view);
68 | } else {
69 | view = LayoutInflater.from(mContext)
70 | .inflate(R.layout.item_message_sent, parent, false);
71 | viewHolder = new SentMessageViewHolder(view);
72 | }
73 |
74 | return viewHolder;
75 | }
76 |
77 | @Override
78 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
79 | ChatMessage chatMessage = mItems.get(position);
80 |
81 | if (chatMessage.getType() == ChatMessage.TYPE_MESSAGE_RECEIVED) {
82 | ((ReceivedMessageViewHolder) holder).tvUsername.setText(chatMessage.getUsername());
83 | ((ReceivedMessageViewHolder) holder).tvMessage.setText(chatMessage.getMessage());
84 | } else {
85 | ((SentMessageViewHolder) holder).tvUsername.setText(chatMessage.getUsername());
86 | ((SentMessageViewHolder) holder).tvMessage.setText(chatMessage.getMessage());
87 | }
88 | }
89 |
90 | @Override
91 | public int getItemCount() {
92 | return mItems.size();
93 | }
94 |
95 | @Override
96 | public int getItemViewType(int position) {
97 | return mItems.get(position).getType();
98 | }
99 |
100 | /**
101 | * Use this method to add new chat message to to the RecyclerView.
102 | *
103 | * @param chatMessage
104 | */
105 | public void addNewMessage(@NonNull ChatMessage chatMessage) {
106 | mItems.add(chatMessage);
107 | notifyItemInserted(mItems.size() - 1);
108 | }
109 |
110 | static class ReceivedMessageViewHolder extends RecyclerView.ViewHolder {
111 | TextView tvUsername, tvMessage;
112 |
113 | public ReceivedMessageViewHolder(View itemView) {
114 | super(itemView);
115 | tvUsername = itemView.findViewById(R.id.tvUsername);
116 | tvMessage = itemView.findViewById(R.id.tvMessage);
117 | }
118 | }
119 |
120 | static class SentMessageViewHolder extends RecyclerView.ViewHolder {
121 | TextView tvUsername, tvMessage;
122 |
123 | public SentMessageViewHolder(View itemView) {
124 | super(itemView);
125 | tvUsername = itemView.findViewById(R.id.tvUsername);
126 | tvMessage = itemView.findViewById(R.id.tvMessage);
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mayurrokade/chatapp/chat/ChatPresenter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Mayur Rokade
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | package com.mayurrokade.chatapp.chat;
24 |
25 | import android.support.annotation.NonNull;
26 |
27 | import com.mayurrokade.chatapp.data.ChatMessage;
28 | import com.mayurrokade.chatapp.data.source.Repository;
29 | import com.mayurrokade.chatapp.eventservice.EventListener;
30 | import com.mayurrokade.chatapp.util.schedulers.BaseSchedulerProvider;
31 |
32 | import java.net.URISyntaxException;
33 |
34 | import io.reactivex.disposables.CompositeDisposable;
35 | import io.reactivex.disposables.Disposable;
36 | import io.reactivex.functions.Consumer;
37 |
38 | /**
39 | * Listens to user actions and sends data to remote data source.
40 | *