├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── easychat │ │ └── ExampleInstrumentedTest.java │ ├── debug │ ├── ic_launcher-playstore.png │ └── res │ │ ├── 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 │ │ └── ic_launcher_background.xml │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── easychat │ │ │ ├── ChatActivity.java │ │ │ ├── ChatFragment.java │ │ │ ├── FCMNotificationService.java │ │ │ ├── LoginOtpActivity.java │ │ │ ├── LoginPhoneNumberActivity.java │ │ │ ├── LoginUsernameActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── ProfileFragment.java │ │ │ ├── SearchUserActivity.java │ │ │ ├── SplashActivity.java │ │ │ ├── adapter │ │ │ ├── ChatRecyclerAdapter.java │ │ │ ├── RecentChatRecyclerAdapter.java │ │ │ └── SearchUserRecyclerAdapter.java │ │ │ ├── model │ │ │ ├── ChatMessageModel.java │ │ │ ├── ChatroomModel.java │ │ │ └── UserModel.java │ │ │ └── utils │ │ │ ├── AndroidUtil.java │ │ │ └── FirebaseUtil.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── btn_rounded_corner.xml │ │ ├── chat_icon.xml │ │ ├── circular_bg.xml │ │ ├── edit_text_rounded_corner.xml │ │ ├── ic_launcher_background.xml │ │ ├── icon_back.xml │ │ ├── icon_search.xml │ │ ├── icon_send.xml │ │ ├── launcher_icon.png │ │ ├── otp_icon.xml │ │ ├── person_icon.xml │ │ └── phone_icon.xml │ │ ├── layout │ │ ├── activity_chat.xml │ │ ├── activity_login_otp.xml │ │ ├── activity_login_phone_number.xml │ │ ├── activity_login_username.xml │ │ ├── activity_main.xml │ │ ├── activity_search_user.xml │ │ ├── activity_splash.xml │ │ ├── chat_message_recycler_row.xml │ │ ├── fragment_chat.xml │ │ ├── fragment_profile.xml │ │ ├── profile_pic_view.xml │ │ ├── recent_chat_recycler_row.xml │ │ └── search_user_recycler_row.xml │ │ ├── menu │ │ └── bottom_navigation_menu.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 │ └── test │ └── java │ └── com │ └── example │ └── easychat │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Firebase Chat App 2 | 3 | Welcome to the Firebase Chat App repository! This app enables real-time chat functionality using Firebase as the backend. Below is a list of key files and their functionalities: 4 | 5 | ## Activity Files 6 | 7 | - `ChatActivity.java`: The main activity for individual chat conversations. 8 | - `LoginOtpActivity.java`: Handles user authentication using OTP. 9 | - `LoginPhoneNumberActivity.java`: Manages phone number-based user login. 10 | - `LoginUsernameActivity.java`: Controls user login using a username. 11 | - `MainActivity.java`: The app's entry point and primary navigation hub. 12 | - `SearchUserActivity.java`: Allows users to search for other users to initiate chats. 13 | - `SplashActivity.java`: Displays a splash screen while the app initializes. 14 | 15 | ## Fragment Files 16 | 17 | - `ChatFragment.java`: Manages chat UI and logic within the chat activity. 18 | - `ProfileFragment.java`: Handles user profile display and editing. 19 | - `SearchUserFragment.java`: Displays user search results and options for starting a chat. 20 | 21 | ## Service File 22 | 23 | - `FCMNotificationService.java`: Integrates Firebase Cloud Messaging for push notifications. 24 | 25 | Feel free to explore these files to understand the structure of the app and how different components interact. The app leverages Firebase Authentication, Realtime Database, and Firebase Cloud Messaging to provide seamless chat functionality. 26 | 27 | ## Getting Started 28 | 29 | To use this app: 30 | 31 | 1. Clone or download the repository. 32 | 2. Set up your Firebase project and update the `google-services.json` file. 33 | 3. Build and run the app on your Android device or emulator. 34 | 35 | ## Notes 36 | 37 | - This repository provides a basic structure for a Firebase-based chat app. You can extend and customize it as per your requirements. 38 | - Make sure to handle security and privacy aspects when implementing user authentication and chat features. 39 | 40 | For more details about Firebase services and Android app development, refer to the official [Firebase Documentation](https://firebase.google.com/docs) and [Android Documentation](https://developer.android.com/docs). 41 | 42 | Happy coding! 43 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'com.google.gms.google-services' 4 | } 5 | 6 | android { 7 | namespace 'com.example.easychat' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.example.easychat" 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 | } 31 | 32 | dependencies { 33 | 34 | implementation 'androidx.appcompat:appcompat:1.6.1' 35 | implementation 'com.google.android.material:material:1.8.0' 36 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 37 | implementation 'com.google.firebase:firebase-firestore-ktx:24.4.4' 38 | implementation 'com.google.firebase:firebase-storage-ktx:20.1.0' 39 | implementation 'com.google.firebase:firebase-messaging-ktx:23.1.2' 40 | testImplementation 'junit:junit:4.13.2' 41 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 42 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 43 | implementation 'com.hbb20:ccp:2.5.0' 44 | 45 | implementation platform('com.google.firebase:firebase-bom:31.3.0') 46 | implementation 'com.google.firebase:firebase-auth' 47 | implementation 'com.firebaseui:firebase-ui-firestore:8.0.2' 48 | implementation 'com.github.dhaval2404:imagepicker:2.1' 49 | implementation 'com.github.bumptech.glide:glide:4.15.1' 50 | implementation("com.squareup.okhttp3:okhttp:4.10.0") 51 | 52 | } -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "745155540833", 4 | "project_id": "easy-chat-backend", 5 | "storage_bucket": "easy-chat-backend.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:745155540833:android:8d8356e6c4a882ecde7720", 11 | "android_client_info": { 12 | "package_name": "com.example.easychat" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "745155540833-fvdk5u97tdl1i3nje2hr4n0pnnlc9q4f.apps.googleusercontent.com", 18 | "client_type": 3 19 | } 20 | ], 21 | "api_key": [ 22 | { 23 | "current_key": "AIzaSyAkXBuJqEYHZJ00fpHgKcLGocgZ3ZXMWo0" 24 | } 25 | ], 26 | "services": { 27 | "appinvite_service": { 28 | "other_platform_oauth_client": [ 29 | { 30 | "client_id": "745155540833-fvdk5u97tdl1i3nje2hr4n0pnnlc9q4f.apps.googleusercontent.com", 31 | "client_type": 3 32 | } 33 | ] 34 | } 35 | } 36 | } 37 | ], 38 | "configuration_version": "1" 39 | } -------------------------------------------------------------------------------- /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/androidTest/java/com/example/easychat/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.example.easychat", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/debug/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/debug/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 16 | 18 | 20 | 21 | 24 | 27 | 28 | 31 | 34 | 35 | 38 | 41 | 42 | 45 | 48 | 49 | 52 | 55 | 56 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 68 | 69 | 72 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/ChatActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import androidx.recyclerview.widget.LinearLayoutManager; 6 | import androidx.recyclerview.widget.RecyclerView; 7 | 8 | import android.net.Uri; 9 | import android.os.Bundle; 10 | import android.util.Log; 11 | import android.widget.EditText; 12 | import android.widget.ImageButton; 13 | import android.widget.ImageView; 14 | import android.widget.TextView; 15 | 16 | import com.example.easychat.adapter.ChatRecyclerAdapter; 17 | import com.example.easychat.adapter.SearchUserRecyclerAdapter; 18 | import com.example.easychat.model.ChatMessageModel; 19 | import com.example.easychat.model.ChatroomModel; 20 | import com.example.easychat.model.UserModel; 21 | import com.example.easychat.utils.AndroidUtil; 22 | import com.example.easychat.utils.FirebaseUtil; 23 | import com.firebase.ui.firestore.FirestoreRecyclerOptions; 24 | import com.google.android.gms.tasks.OnCompleteListener; 25 | import com.google.android.gms.tasks.Task; 26 | import com.google.firebase.Timestamp; 27 | import com.google.firebase.firestore.DocumentReference; 28 | import com.google.firebase.firestore.DocumentSnapshot; 29 | import com.google.firebase.firestore.Query; 30 | 31 | import org.checkerframework.checker.units.qual.C; 32 | import org.json.JSONObject; 33 | 34 | import java.io.IOException; 35 | import java.sql.Time; 36 | import java.util.Arrays; 37 | 38 | import okhttp3.Call; 39 | import okhttp3.Callback; 40 | import okhttp3.MediaType; 41 | import okhttp3.OkHttpClient; 42 | import okhttp3.Request; 43 | import okhttp3.RequestBody; 44 | import okhttp3.Response; 45 | 46 | public class ChatActivity extends AppCompatActivity { 47 | 48 | UserModel otherUser; 49 | String chatroomId; 50 | ChatroomModel chatroomModel; 51 | ChatRecyclerAdapter adapter; 52 | 53 | EditText messageInput; 54 | ImageButton sendMessageBtn; 55 | ImageButton backBtn; 56 | TextView otherUsername; 57 | RecyclerView recyclerView; 58 | ImageView imageView; 59 | 60 | 61 | @Override 62 | protected void onCreate(Bundle savedInstanceState) { 63 | super.onCreate(savedInstanceState); 64 | setContentView(R.layout.activity_chat); 65 | 66 | //get UserModel 67 | otherUser = AndroidUtil.getUserModelFromIntent(getIntent()); 68 | chatroomId = FirebaseUtil.getChatroomId(FirebaseUtil.currentUserId(),otherUser.getUserId()); 69 | 70 | messageInput = findViewById(R.id.chat_message_input); 71 | sendMessageBtn = findViewById(R.id.message_send_btn); 72 | backBtn = findViewById(R.id.back_btn); 73 | otherUsername = findViewById(R.id.other_username); 74 | recyclerView = findViewById(R.id.chat_recycler_view); 75 | imageView = findViewById(R.id.profile_pic_image_view); 76 | 77 | FirebaseUtil.getOtherProfilePicStorageRef(otherUser.getUserId()).getDownloadUrl() 78 | .addOnCompleteListener(t -> { 79 | if(t.isSuccessful()){ 80 | Uri uri = t.getResult(); 81 | AndroidUtil.setProfilePic(this,uri,imageView); 82 | } 83 | }); 84 | 85 | backBtn.setOnClickListener((v)->{ 86 | onBackPressed(); 87 | }); 88 | otherUsername.setText(otherUser.getUsername()); 89 | 90 | sendMessageBtn.setOnClickListener((v -> { 91 | String message = messageInput.getText().toString().trim(); 92 | if(message.isEmpty()) 93 | return; 94 | sendMessageToUser(message); 95 | })); 96 | 97 | getOrCreateChatroomModel(); 98 | setupChatRecyclerView(); 99 | } 100 | 101 | void setupChatRecyclerView(){ 102 | Query query = FirebaseUtil.getChatroomMessageReference(chatroomId) 103 | .orderBy("timestamp", Query.Direction.DESCENDING); 104 | 105 | FirestoreRecyclerOptions options = new FirestoreRecyclerOptions.Builder() 106 | .setQuery(query,ChatMessageModel.class).build(); 107 | 108 | adapter = new ChatRecyclerAdapter(options,getApplicationContext()); 109 | LinearLayoutManager manager = new LinearLayoutManager(this); 110 | manager.setReverseLayout(true); 111 | recyclerView.setLayoutManager(manager); 112 | recyclerView.setAdapter(adapter); 113 | adapter.startListening(); 114 | adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { 115 | @Override 116 | public void onItemRangeInserted(int positionStart, int itemCount) { 117 | super.onItemRangeInserted(positionStart, itemCount); 118 | recyclerView.smoothScrollToPosition(0); 119 | } 120 | }); 121 | } 122 | 123 | void sendMessageToUser(String message){ 124 | 125 | chatroomModel.setLastMessageTimestamp(Timestamp.now()); 126 | chatroomModel.setLastMessageSenderId(FirebaseUtil.currentUserId()); 127 | chatroomModel.setLastMessage(message); 128 | FirebaseUtil.getChatroomReference(chatroomId).set(chatroomModel); 129 | 130 | ChatMessageModel chatMessageModel = new ChatMessageModel(message,FirebaseUtil.currentUserId(),Timestamp.now()); 131 | FirebaseUtil.getChatroomMessageReference(chatroomId).add(chatMessageModel) 132 | .addOnCompleteListener(new OnCompleteListener() { 133 | @Override 134 | public void onComplete(@NonNull Task task) { 135 | if(task.isSuccessful()){ 136 | messageInput.setText(""); 137 | sendNotification(message); 138 | } 139 | } 140 | }); 141 | } 142 | 143 | void getOrCreateChatroomModel(){ 144 | FirebaseUtil.getChatroomReference(chatroomId).get().addOnCompleteListener(task -> { 145 | if(task.isSuccessful()){ 146 | chatroomModel = task.getResult().toObject(ChatroomModel.class); 147 | if(chatroomModel==null){ 148 | //first time chat 149 | chatroomModel = new ChatroomModel( 150 | chatroomId, 151 | Arrays.asList(FirebaseUtil.currentUserId(),otherUser.getUserId()), 152 | Timestamp.now(), 153 | "" 154 | ); 155 | FirebaseUtil.getChatroomReference(chatroomId).set(chatroomModel); 156 | } 157 | } 158 | }); 159 | } 160 | 161 | void sendNotification(String message){ 162 | 163 | FirebaseUtil.currentUserDetails().get().addOnCompleteListener(task -> { 164 | if(task.isSuccessful()){ 165 | UserModel currentUser = task.getResult().toObject(UserModel.class); 166 | try{ 167 | JSONObject jsonObject = new JSONObject(); 168 | 169 | JSONObject notificationObj = new JSONObject(); 170 | notificationObj.put("title",currentUser.getUsername()); 171 | notificationObj.put("body",message); 172 | 173 | JSONObject dataObj = new JSONObject(); 174 | dataObj.put("userId",currentUser.getUserId()); 175 | 176 | jsonObject.put("notification",notificationObj); 177 | jsonObject.put("data",dataObj); 178 | jsonObject.put("to",otherUser.getFcmToken()); 179 | 180 | callApi(jsonObject); 181 | 182 | 183 | }catch (Exception e){ 184 | 185 | } 186 | 187 | } 188 | }); 189 | 190 | } 191 | 192 | void callApi(JSONObject jsonObject){ 193 | MediaType JSON = MediaType.get("application/json; charset=utf-8"); 194 | OkHttpClient client = new OkHttpClient(); 195 | String url = "https://fcm.googleapis.com/fcm/send"; 196 | RequestBody body = RequestBody.create(jsonObject.toString(),JSON); 197 | Request request = new Request.Builder() 198 | .url(url) 199 | .post(body) 200 | .header("Authorization","Bearer YOUR_API_KEY") 201 | .build(); 202 | client.newCall(request).enqueue(new Callback() { 203 | @Override 204 | public void onFailure(@NonNull Call call, @NonNull IOException e) { 205 | 206 | } 207 | 208 | @Override 209 | public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { 210 | 211 | } 212 | }); 213 | 214 | } 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/ChatFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.fragment.app.Fragment; 6 | import androidx.recyclerview.widget.LinearLayoutManager; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | 13 | import com.example.easychat.adapter.RecentChatRecyclerAdapter; 14 | import com.example.easychat.adapter.SearchUserRecyclerAdapter; 15 | import com.example.easychat.model.ChatroomModel; 16 | import com.example.easychat.model.UserModel; 17 | import com.example.easychat.utils.FirebaseUtil; 18 | import com.firebase.ui.firestore.FirestoreRecyclerOptions; 19 | import com.google.firebase.firestore.Query; 20 | 21 | public class ChatFragment extends Fragment { 22 | 23 | RecyclerView recyclerView; 24 | RecentChatRecyclerAdapter adapter; 25 | 26 | 27 | public ChatFragment() { 28 | } 29 | @Override 30 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 31 | Bundle savedInstanceState) { 32 | View view = inflater.inflate(R.layout.fragment_chat, container, false); 33 | recyclerView = view.findViewById(R.id.recyler_view); 34 | setupRecyclerView(); 35 | 36 | return view; 37 | } 38 | 39 | void setupRecyclerView(){ 40 | 41 | Query query = FirebaseUtil.allChatroomCollectionReference() 42 | .whereArrayContains("userIds",FirebaseUtil.currentUserId()) 43 | .orderBy("lastMessageTimestamp",Query.Direction.DESCENDING); 44 | 45 | FirestoreRecyclerOptions options = new FirestoreRecyclerOptions.Builder() 46 | .setQuery(query,ChatroomModel.class).build(); 47 | 48 | adapter = new RecentChatRecyclerAdapter(options,getContext()); 49 | recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 50 | recyclerView.setAdapter(adapter); 51 | adapter.startListening(); 52 | 53 | } 54 | 55 | @Override 56 | public void onStart() { 57 | super.onStart(); 58 | if(adapter!=null) 59 | adapter.startListening(); 60 | } 61 | 62 | @Override 63 | public void onStop() { 64 | super.onStop(); 65 | if(adapter!=null) 66 | adapter.stopListening(); 67 | } 68 | 69 | @Override 70 | public void onResume() { 71 | super.onResume(); 72 | if(adapter!=null) 73 | adapter.notifyDataSetChanged(); 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/FCMNotificationService.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import com.google.firebase.messaging.FirebaseMessagingService; 4 | 5 | public class FCMNotificationService extends FirebaseMessagingService { 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/LoginOtpActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.Button; 10 | import android.widget.EditText; 11 | import android.widget.ProgressBar; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | import com.example.easychat.utils.AndroidUtil; 16 | import com.google.android.gms.tasks.OnCompleteListener; 17 | import com.google.android.gms.tasks.Task; 18 | import com.google.firebase.FirebaseException; 19 | import com.google.firebase.auth.AuthResult; 20 | import com.google.firebase.auth.FirebaseAuth; 21 | import com.google.firebase.auth.PhoneAuthCredential; 22 | import com.google.firebase.auth.PhoneAuthOptions; 23 | import com.google.firebase.auth.PhoneAuthProvider; 24 | import com.google.firebase.firestore.FirebaseFirestore; 25 | 26 | import org.w3c.dom.Text; 27 | 28 | import java.util.HashMap; 29 | import java.util.Map; 30 | import java.util.Timer; 31 | import java.util.TimerTask; 32 | import java.util.concurrent.TimeUnit; 33 | 34 | public class LoginOtpActivity extends AppCompatActivity { 35 | 36 | String phoneNumber; 37 | Long timeoutSeconds = 60L; 38 | String verificationCode; 39 | PhoneAuthProvider.ForceResendingToken resendingToken; 40 | 41 | EditText otpInput; 42 | Button nextBtn; 43 | ProgressBar progressBar; 44 | TextView resendOtpTextView; 45 | FirebaseAuth mAuth = FirebaseAuth.getInstance(); 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | setContentView(R.layout.activity_login_otp); 51 | 52 | otpInput = findViewById(R.id.login_otp); 53 | nextBtn = findViewById(R.id.login_next_btn); 54 | progressBar = findViewById(R.id.login_progress_bar); 55 | resendOtpTextView = findViewById(R.id.resend_otp_textview); 56 | 57 | phoneNumber = getIntent().getExtras().getString("phone"); 58 | 59 | sendOtp(phoneNumber,false); 60 | 61 | nextBtn.setOnClickListener(v -> { 62 | String enteredOtp = otpInput.getText().toString(); 63 | PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationCode,enteredOtp); 64 | signIn(credential); 65 | }); 66 | 67 | resendOtpTextView.setOnClickListener((v)->{ 68 | sendOtp(phoneNumber,true); 69 | }); 70 | 71 | } 72 | 73 | void sendOtp(String phoneNumber,boolean isResend){ 74 | startResendTimer(); 75 | setInProgress(true); 76 | PhoneAuthOptions.Builder builder = 77 | PhoneAuthOptions.newBuilder(mAuth) 78 | .setPhoneNumber(phoneNumber) 79 | .setTimeout(timeoutSeconds, TimeUnit.SECONDS) 80 | .setActivity(this) 81 | .setCallbacks(new PhoneAuthProvider.OnVerificationStateChangedCallbacks() { 82 | @Override 83 | public void onVerificationCompleted(@NonNull PhoneAuthCredential phoneAuthCredential) { 84 | signIn(phoneAuthCredential); 85 | setInProgress(false); 86 | } 87 | 88 | @Override 89 | public void onVerificationFailed(@NonNull FirebaseException e) { 90 | AndroidUtil.showToast(getApplicationContext(),"OTP verification failed"); 91 | setInProgress(false); 92 | } 93 | 94 | @Override 95 | public void onCodeSent(@NonNull String s, @NonNull PhoneAuthProvider.ForceResendingToken forceResendingToken) { 96 | super.onCodeSent(s, forceResendingToken); 97 | verificationCode = s; 98 | resendingToken = forceResendingToken; 99 | AndroidUtil.showToast(getApplicationContext(),"OTP sent successfully"); 100 | setInProgress(false); 101 | } 102 | }); 103 | if(isResend){ 104 | PhoneAuthProvider.verifyPhoneNumber(builder.setForceResendingToken(resendingToken).build()); 105 | }else{ 106 | PhoneAuthProvider.verifyPhoneNumber(builder.build()); 107 | } 108 | 109 | } 110 | 111 | void setInProgress(boolean inProgress){ 112 | if(inProgress){ 113 | progressBar.setVisibility(View.VISIBLE); 114 | nextBtn.setVisibility(View.GONE); 115 | }else{ 116 | progressBar.setVisibility(View.GONE); 117 | nextBtn.setVisibility(View.VISIBLE); 118 | } 119 | } 120 | 121 | void signIn(PhoneAuthCredential phoneAuthCredential){ 122 | //login and go to next activity 123 | setInProgress(true); 124 | mAuth.signInWithCredential(phoneAuthCredential).addOnCompleteListener(new OnCompleteListener() { 125 | @Override 126 | public void onComplete(@NonNull Task task) { 127 | setInProgress(false); 128 | if(task.isSuccessful()){ 129 | Intent intent = new Intent(LoginOtpActivity.this,LoginUsernameActivity.class); 130 | intent.putExtra("phone",phoneNumber); 131 | startActivity(intent); 132 | }else{ 133 | AndroidUtil.showToast(getApplicationContext(),"OTP verification failed"); 134 | } 135 | } 136 | }); 137 | 138 | 139 | } 140 | 141 | void startResendTimer(){ 142 | resendOtpTextView.setEnabled(false); 143 | Timer timer = new Timer(); 144 | timer.scheduleAtFixedRate(new TimerTask() { 145 | @Override 146 | public void run() { 147 | timeoutSeconds--; 148 | resendOtpTextView.setText("Resend OTP in "+timeoutSeconds +" seconds"); 149 | if(timeoutSeconds<=0){ 150 | timeoutSeconds =60L; 151 | timer.cancel(); 152 | runOnUiThread(() -> { 153 | resendOtpTextView.setEnabled(true); 154 | }); 155 | } 156 | } 157 | },0,1000); 158 | } 159 | 160 | 161 | } 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/LoginPhoneNumberActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.EditText; 10 | import android.widget.ProgressBar; 11 | 12 | import com.hbb20.CountryCodePicker; 13 | 14 | public class LoginPhoneNumberActivity extends AppCompatActivity { 15 | 16 | CountryCodePicker countryCodePicker; 17 | EditText phoneInput; 18 | Button sendOtpBtn; 19 | ProgressBar progressBar; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_login_phone_number); 25 | 26 | countryCodePicker = findViewById(R.id.login_countrycode); 27 | phoneInput = findViewById(R.id.login_mobile_number); 28 | sendOtpBtn = findViewById(R.id.send_otp_btn); 29 | progressBar = findViewById(R.id.login_progress_bar); 30 | 31 | progressBar.setVisibility(View.GONE); 32 | 33 | countryCodePicker.registerCarrierNumberEditText(phoneInput); 34 | sendOtpBtn.setOnClickListener((v)->{ 35 | if(!countryCodePicker.isValidFullNumber()){ 36 | phoneInput.setError("Phone number not valid"); 37 | return; 38 | } 39 | Intent intent = new Intent(LoginPhoneNumberActivity.this,LoginOtpActivity.class); 40 | intent.putExtra("phone",countryCodePicker.getFullNumberWithPlus()); 41 | startActivity(intent); 42 | }); 43 | } 44 | 45 | 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/LoginUsernameActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.Button; 10 | import android.widget.EditText; 11 | import android.widget.ProgressBar; 12 | 13 | import com.example.easychat.model.UserModel; 14 | import com.example.easychat.utils.FirebaseUtil; 15 | import com.google.android.gms.tasks.OnCompleteListener; 16 | import com.google.android.gms.tasks.Task; 17 | import com.google.firebase.Timestamp; 18 | import com.google.firebase.firestore.DocumentSnapshot; 19 | 20 | public class LoginUsernameActivity extends AppCompatActivity { 21 | 22 | EditText usernameInput; 23 | Button letMeInBtn; 24 | ProgressBar progressBar; 25 | String phoneNumber; 26 | UserModel userModel; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_login_username); 32 | 33 | usernameInput = findViewById(R.id.login_username); 34 | letMeInBtn = findViewById(R.id.login_let_me_in_btn); 35 | progressBar =findViewById(R.id.login_progress_bar); 36 | 37 | phoneNumber = getIntent().getExtras().getString("phone"); 38 | getUsername(); 39 | 40 | letMeInBtn.setOnClickListener((v -> { 41 | setUsername(); 42 | })); 43 | 44 | 45 | } 46 | 47 | void setUsername(){ 48 | 49 | String username = usernameInput.getText().toString(); 50 | if(username.isEmpty() || username.length()<3){ 51 | usernameInput.setError("Username length should be at least 3 chars"); 52 | return; 53 | } 54 | setInProgress(true); 55 | if(userModel!=null){ 56 | userModel.setUsername(username); 57 | }else{ 58 | userModel = new UserModel(phoneNumber,username, Timestamp.now(),FirebaseUtil.currentUserId()); 59 | } 60 | 61 | FirebaseUtil.currentUserDetails().set(userModel).addOnCompleteListener(new OnCompleteListener() { 62 | @Override 63 | public void onComplete(@NonNull Task task) { 64 | setInProgress(false); 65 | if(task.isSuccessful()){ 66 | Intent intent = new Intent(LoginUsernameActivity.this,MainActivity.class); 67 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK ); 68 | startActivity(intent); 69 | } 70 | } 71 | }); 72 | 73 | } 74 | 75 | void getUsername(){ 76 | setInProgress(true); 77 | FirebaseUtil.currentUserDetails().get().addOnCompleteListener(new OnCompleteListener() { 78 | @Override 79 | public void onComplete(@NonNull Task task) { 80 | setInProgress(false); 81 | if(task.isSuccessful()){ 82 | userModel = task.getResult().toObject(UserModel.class); 83 | if(userModel!=null){ 84 | usernameInput.setText(userModel.getUsername()); 85 | } 86 | } 87 | } 88 | }); 89 | } 90 | 91 | void setInProgress(boolean inProgress){ 92 | if(inProgress){ 93 | progressBar.setVisibility(View.VISIBLE); 94 | letMeInBtn.setVisibility(View.GONE); 95 | }else{ 96 | progressBar.setVisibility(View.GONE); 97 | letMeInBtn.setVisibility(View.VISIBLE); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.util.Log; 9 | import android.view.MenuItem; 10 | import android.widget.ImageButton; 11 | 12 | import com.example.easychat.utils.FirebaseUtil; 13 | import com.google.android.gms.tasks.OnCompleteListener; 14 | import com.google.android.gms.tasks.Task; 15 | import com.google.android.material.bottomnavigation.BottomNavigationView; 16 | import com.google.android.material.navigation.NavigationBarView; 17 | import com.google.firebase.messaging.FirebaseMessaging; 18 | 19 | public class MainActivity extends AppCompatActivity { 20 | 21 | BottomNavigationView bottomNavigationView; 22 | ImageButton searchButton; 23 | 24 | ChatFragment chatFragment; 25 | ProfileFragment profileFragment; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_main); 31 | 32 | chatFragment = new ChatFragment(); 33 | profileFragment = new ProfileFragment(); 34 | 35 | bottomNavigationView = findViewById(R.id.bottom_navigation); 36 | searchButton = findViewById(R.id.main_search_btn); 37 | 38 | searchButton.setOnClickListener((v)->{ 39 | startActivity(new Intent(MainActivity.this,SearchUserActivity.class)); 40 | }); 41 | 42 | bottomNavigationView.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() { 43 | @Override 44 | public boolean onNavigationItemSelected(@NonNull MenuItem item) { 45 | if(item.getItemId()==R.id.menu_chat){ 46 | getSupportFragmentManager().beginTransaction().replace(R.id.main_frame_layout,chatFragment).commit(); 47 | } 48 | if(item.getItemId()==R.id.menu_profile){ 49 | getSupportFragmentManager().beginTransaction().replace(R.id.main_frame_layout,profileFragment).commit(); 50 | } 51 | return true; 52 | } 53 | }); 54 | bottomNavigationView.setSelectedItemId(R.id.menu_chat); 55 | 56 | getFCMToken(); 57 | 58 | } 59 | 60 | void getFCMToken(){ 61 | FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> { 62 | if(task.isSuccessful()){ 63 | String token = task.getResult(); 64 | FirebaseUtil.currentUserDetails().update("fcmToken",token); 65 | 66 | } 67 | }); 68 | } 69 | } 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/ProfileFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | 8 | import androidx.activity.result.ActivityResultLauncher; 9 | import androidx.activity.result.contract.ActivityResultContracts; 10 | import androidx.annotation.NonNull; 11 | import androidx.annotation.Nullable; 12 | import androidx.fragment.app.Fragment; 13 | 14 | import android.view.LayoutInflater; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.widget.Button; 18 | import android.widget.EditText; 19 | import android.widget.ImageView; 20 | import android.widget.ProgressBar; 21 | import android.widget.TextView; 22 | 23 | import com.example.easychat.model.UserModel; 24 | import com.example.easychat.utils.AndroidUtil; 25 | import com.example.easychat.utils.FirebaseUtil; 26 | import com.github.dhaval2404.imagepicker.ImagePicker; 27 | import com.google.android.gms.tasks.OnCompleteListener; 28 | import com.google.android.gms.tasks.Task; 29 | import com.google.firebase.firestore.DocumentSnapshot; 30 | import com.google.firebase.messaging.FirebaseMessaging; 31 | import com.google.firebase.storage.UploadTask; 32 | 33 | import kotlin.Unit; 34 | import kotlin.jvm.functions.Function1; 35 | 36 | public class ProfileFragment extends Fragment { 37 | 38 | ImageView profilePic; 39 | EditText usernameInput; 40 | EditText phoneInput; 41 | Button updateProfileBtn; 42 | ProgressBar progressBar; 43 | TextView logoutBtn; 44 | 45 | UserModel currentUserModel; 46 | ActivityResultLauncher imagePickLauncher; 47 | Uri selectedImageUri; 48 | 49 | public ProfileFragment() { 50 | 51 | } 52 | 53 | @Override 54 | public void onCreate(@Nullable Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | imagePickLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), 57 | result -> { 58 | if(result.getResultCode() == Activity.RESULT_OK){ 59 | Intent data = result.getData(); 60 | if(data!=null && data.getData()!=null){ 61 | selectedImageUri = data.getData(); 62 | AndroidUtil.setProfilePic(getContext(),selectedImageUri,profilePic); 63 | } 64 | } 65 | } 66 | ); 67 | } 68 | 69 | @Override 70 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 71 | Bundle savedInstanceState) { 72 | View view = inflater.inflate(R.layout.fragment_profile, container, false); 73 | profilePic = view.findViewById(R.id.profile_image_view); 74 | usernameInput = view.findViewById(R.id.profile_username); 75 | phoneInput = view.findViewById(R.id.profile_phone); 76 | updateProfileBtn = view.findViewById(R.id.profle_update_btn); 77 | progressBar = view.findViewById(R.id.profile_progress_bar); 78 | logoutBtn = view.findViewById(R.id.logout_btn); 79 | 80 | getUserData(); 81 | 82 | updateProfileBtn.setOnClickListener((v -> { 83 | updateBtnClick(); 84 | })); 85 | 86 | logoutBtn.setOnClickListener((v)->{ 87 | FirebaseMessaging.getInstance().deleteToken().addOnCompleteListener(new OnCompleteListener() { 88 | @Override 89 | public void onComplete(@NonNull Task task) { 90 | if(task.isSuccessful()){ 91 | FirebaseUtil.logout(); 92 | Intent intent = new Intent(getContext(),SplashActivity.class); 93 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 94 | startActivity(intent); 95 | } 96 | } 97 | }); 98 | 99 | 100 | 101 | }); 102 | 103 | profilePic.setOnClickListener((v)->{ 104 | ImagePicker.with(this).cropSquare().compress(512).maxResultSize(512,512) 105 | .createIntent(new Function1() { 106 | @Override 107 | public Unit invoke(Intent intent) { 108 | imagePickLauncher.launch(intent); 109 | return null; 110 | } 111 | }); 112 | }); 113 | 114 | return view; 115 | } 116 | 117 | void updateBtnClick(){ 118 | String newUsername = usernameInput.getText().toString(); 119 | if(newUsername.isEmpty() || newUsername.length()<3){ 120 | usernameInput.setError("Username length should be at least 3 chars"); 121 | return; 122 | } 123 | currentUserModel.setUsername(newUsername); 124 | setInProgress(true); 125 | 126 | 127 | if(selectedImageUri!=null){ 128 | FirebaseUtil.getCurrentProfilePicStorageRef().putFile(selectedImageUri) 129 | .addOnCompleteListener(task -> { 130 | updateToFirestore(); 131 | }); 132 | }else{ 133 | updateToFirestore(); 134 | } 135 | 136 | 137 | 138 | 139 | 140 | } 141 | 142 | void updateToFirestore(){ 143 | FirebaseUtil.currentUserDetails().set(currentUserModel) 144 | .addOnCompleteListener(task -> { 145 | setInProgress(false); 146 | if(task.isSuccessful()){ 147 | AndroidUtil.showToast(getContext(),"Updated successfully"); 148 | }else{ 149 | AndroidUtil.showToast(getContext(),"Updated failed"); 150 | } 151 | }); 152 | } 153 | 154 | 155 | 156 | void getUserData(){ 157 | setInProgress(true); 158 | 159 | FirebaseUtil.getCurrentProfilePicStorageRef().getDownloadUrl() 160 | .addOnCompleteListener(task -> { 161 | if(task.isSuccessful()){ 162 | Uri uri = task.getResult(); 163 | AndroidUtil.setProfilePic(getContext(),uri,profilePic); 164 | } 165 | }); 166 | 167 | FirebaseUtil.currentUserDetails().get().addOnCompleteListener(task -> { 168 | setInProgress(false); 169 | currentUserModel = task.getResult().toObject(UserModel.class); 170 | usernameInput.setText(currentUserModel.getUsername()); 171 | phoneInput.setText(currentUserModel.getPhone()); 172 | }); 173 | } 174 | 175 | 176 | void setInProgress(boolean inProgress){ 177 | if(inProgress){ 178 | progressBar.setVisibility(View.VISIBLE); 179 | updateProfileBtn.setVisibility(View.GONE); 180 | }else{ 181 | progressBar.setVisibility(View.GONE); 182 | updateProfileBtn.setVisibility(View.VISIBLE); 183 | } 184 | } 185 | } 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/SearchUserActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import androidx.recyclerview.widget.LinearLayoutManager; 5 | import androidx.recyclerview.widget.RecyclerView; 6 | 7 | import android.os.Bundle; 8 | import android.widget.EditText; 9 | import android.widget.ImageButton; 10 | 11 | import com.example.easychat.adapter.SearchUserRecyclerAdapter; 12 | import com.example.easychat.model.UserModel; 13 | import com.example.easychat.utils.FirebaseUtil; 14 | import com.firebase.ui.firestore.FirestoreRecyclerOptions; 15 | import com.google.firebase.firestore.Query; 16 | 17 | public class SearchUserActivity extends AppCompatActivity { 18 | 19 | EditText searchInput; 20 | ImageButton searchButton; 21 | ImageButton backButton; 22 | RecyclerView recyclerView; 23 | 24 | SearchUserRecyclerAdapter adapter; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_search_user); 30 | 31 | searchInput = findViewById(R.id.seach_username_input); 32 | searchButton = findViewById(R.id.search_user_btn); 33 | backButton = findViewById(R.id.back_btn); 34 | recyclerView = findViewById(R.id.search_user_recycler_view); 35 | 36 | searchInput.requestFocus(); 37 | 38 | 39 | backButton.setOnClickListener(v -> { 40 | onBackPressed(); 41 | }); 42 | 43 | searchButton.setOnClickListener(v -> { 44 | String searchTerm = searchInput.getText().toString(); 45 | if(searchTerm.isEmpty() || searchTerm.length()<3){ 46 | searchInput.setError("Invalid Username"); 47 | return; 48 | } 49 | setupSearchRecyclerView(searchTerm); 50 | }); 51 | } 52 | 53 | void setupSearchRecyclerView(String searchTerm){ 54 | 55 | Query query = FirebaseUtil.allUserCollectionReference() 56 | .whereGreaterThanOrEqualTo("username",searchTerm) 57 | .whereLessThanOrEqualTo("username",searchTerm+'\uf8ff'); 58 | 59 | FirestoreRecyclerOptions options = new FirestoreRecyclerOptions.Builder() 60 | .setQuery(query,UserModel.class).build(); 61 | 62 | adapter = new SearchUserRecyclerAdapter(options,getApplicationContext()); 63 | recyclerView.setLayoutManager(new LinearLayoutManager(this)); 64 | recyclerView.setAdapter(adapter); 65 | adapter.startListening(); 66 | 67 | } 68 | 69 | @Override 70 | protected void onStart() { 71 | super.onStart(); 72 | if(adapter!=null) 73 | adapter.startListening(); 74 | } 75 | 76 | @Override 77 | protected void onStop() { 78 | super.onStop(); 79 | if(adapter!=null) 80 | adapter.stopListening(); 81 | } 82 | 83 | @Override 84 | protected void onResume() { 85 | super.onResume(); 86 | if(adapter!=null) 87 | adapter.startListening(); 88 | } 89 | } 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.os.Handler; 9 | 10 | import com.example.easychat.model.UserModel; 11 | import com.example.easychat.utils.AndroidUtil; 12 | import com.example.easychat.utils.FirebaseUtil; 13 | import com.google.android.gms.tasks.OnCompleteListener; 14 | import com.google.android.gms.tasks.Task; 15 | import com.google.firebase.firestore.DocumentSnapshot; 16 | 17 | public class SplashActivity extends AppCompatActivity { 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_splash); 23 | 24 | if(getIntent().getExtras()!=null){ 25 | //from notification 26 | String userId = getIntent().getExtras().getString("userId"); 27 | FirebaseUtil.allUserCollectionReference().document(userId).get() 28 | .addOnCompleteListener(task -> { 29 | if(task.isSuccessful()){ 30 | UserModel model = task.getResult().toObject(UserModel.class); 31 | 32 | Intent mainIntent = new Intent(this,MainActivity.class); 33 | mainIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 34 | startActivity(mainIntent); 35 | 36 | Intent intent = new Intent(this, ChatActivity.class); 37 | AndroidUtil.passUserModelAsIntent(intent,model); 38 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 39 | startActivity(intent); 40 | finish(); 41 | } 42 | }); 43 | 44 | 45 | }else{ 46 | new Handler().postDelayed(new Runnable() { 47 | @Override 48 | public void run() { 49 | if(FirebaseUtil.isLoggedIn()){ 50 | startActivity(new Intent(SplashActivity.this,MainActivity.class)); 51 | }else{ 52 | startActivity(new Intent(SplashActivity.this,LoginPhoneNumberActivity.class)); 53 | } 54 | finish(); 55 | } 56 | },1000); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/adapter/ChatRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.util.Log; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.LinearLayout; 11 | import android.widget.TextView; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.recyclerview.widget.RecyclerView; 15 | 16 | import com.example.easychat.ChatActivity; 17 | import com.example.easychat.R; 18 | import com.example.easychat.model.ChatMessageModel; 19 | import com.example.easychat.utils.AndroidUtil; 20 | import com.example.easychat.utils.FirebaseUtil; 21 | import com.firebase.ui.firestore.FirestoreRecyclerAdapter; 22 | import com.firebase.ui.firestore.FirestoreRecyclerOptions; 23 | 24 | public class ChatRecyclerAdapter extends FirestoreRecyclerAdapter { 25 | 26 | Context context; 27 | 28 | public ChatRecyclerAdapter(@NonNull FirestoreRecyclerOptions options,Context context) { 29 | super(options); 30 | this.context = context; 31 | } 32 | 33 | @Override 34 | protected void onBindViewHolder(@NonNull ChatModelViewHolder holder, int position, @NonNull ChatMessageModel model) { 35 | Log.i("haushd","asjd"); 36 | if(model.getSenderId().equals(FirebaseUtil.currentUserId())){ 37 | holder.leftChatLayout.setVisibility(View.GONE); 38 | holder.rightChatLayout.setVisibility(View.VISIBLE); 39 | holder.rightChatTextview.setText(model.getMessage()); 40 | }else{ 41 | holder.rightChatLayout.setVisibility(View.GONE); 42 | holder.leftChatLayout.setVisibility(View.VISIBLE); 43 | holder.leftChatTextview.setText(model.getMessage()); 44 | } 45 | } 46 | 47 | @NonNull 48 | @Override 49 | public ChatModelViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 50 | View view = LayoutInflater.from(context).inflate(R.layout.chat_message_recycler_row,parent,false); 51 | return new ChatModelViewHolder(view); 52 | } 53 | 54 | class ChatModelViewHolder extends RecyclerView.ViewHolder{ 55 | 56 | LinearLayout leftChatLayout,rightChatLayout; 57 | TextView leftChatTextview,rightChatTextview; 58 | 59 | public ChatModelViewHolder(@NonNull View itemView) { 60 | super(itemView); 61 | 62 | leftChatLayout = itemView.findViewById(R.id.left_chat_layout); 63 | rightChatLayout = itemView.findViewById(R.id.right_chat_layout); 64 | leftChatTextview = itemView.findViewById(R.id.left_chat_textview); 65 | rightChatTextview = itemView.findViewById(R.id.right_chat_textview); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/adapter/RecentChatRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.RelativeLayout; 11 | import android.widget.TextView; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.recyclerview.widget.RecyclerView; 15 | 16 | import com.example.easychat.ChatActivity; 17 | import com.example.easychat.R; 18 | import com.example.easychat.model.ChatroomModel; 19 | import com.example.easychat.model.UserModel; 20 | import com.example.easychat.utils.AndroidUtil; 21 | import com.example.easychat.utils.FirebaseUtil; 22 | import com.firebase.ui.firestore.FirestoreRecyclerAdapter; 23 | import com.firebase.ui.firestore.FirestoreRecyclerOptions; 24 | import com.google.android.gms.tasks.OnCompleteListener; 25 | import com.google.android.gms.tasks.Task; 26 | import com.google.firebase.firestore.DocumentSnapshot; 27 | 28 | public class RecentChatRecyclerAdapter extends FirestoreRecyclerAdapter { 29 | 30 | Context context; 31 | 32 | public RecentChatRecyclerAdapter(@NonNull FirestoreRecyclerOptions options,Context context) { 33 | super(options); 34 | this.context = context; 35 | } 36 | 37 | @Override 38 | protected void onBindViewHolder(@NonNull ChatroomModelViewHolder holder, int position, @NonNull ChatroomModel model) { 39 | FirebaseUtil.getOtherUserFromChatroom(model.getUserIds()) 40 | .get().addOnCompleteListener(task -> { 41 | if(task.isSuccessful()){ 42 | boolean lastMessageSentByMe = model.getLastMessageSenderId().equals(FirebaseUtil.currentUserId()); 43 | 44 | 45 | UserModel otherUserModel = task.getResult().toObject(UserModel.class); 46 | 47 | FirebaseUtil.getOtherProfilePicStorageRef(otherUserModel.getUserId()).getDownloadUrl() 48 | .addOnCompleteListener(t -> { 49 | if(t.isSuccessful()){ 50 | Uri uri = t.getResult(); 51 | AndroidUtil.setProfilePic(context,uri,holder.profilePic); 52 | } 53 | }); 54 | 55 | holder.usernameText.setText(otherUserModel.getUsername()); 56 | if(lastMessageSentByMe) 57 | holder.lastMessageText.setText("You : "+model.getLastMessage()); 58 | else 59 | holder.lastMessageText.setText(model.getLastMessage()); 60 | holder.lastMessageTime.setText(FirebaseUtil.timestampToString(model.getLastMessageTimestamp())); 61 | 62 | holder.itemView.setOnClickListener(v -> { 63 | //navigate to chat activity 64 | Intent intent = new Intent(context, ChatActivity.class); 65 | AndroidUtil.passUserModelAsIntent(intent,otherUserModel); 66 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 67 | context.startActivity(intent); 68 | }); 69 | 70 | } 71 | }); 72 | } 73 | 74 | @NonNull 75 | @Override 76 | public ChatroomModelViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 77 | View view = LayoutInflater.from(context).inflate(R.layout.recent_chat_recycler_row,parent,false); 78 | return new ChatroomModelViewHolder(view); 79 | } 80 | 81 | class ChatroomModelViewHolder extends RecyclerView.ViewHolder{ 82 | TextView usernameText; 83 | TextView lastMessageText; 84 | TextView lastMessageTime; 85 | ImageView profilePic; 86 | 87 | public ChatroomModelViewHolder(@NonNull View itemView) { 88 | super(itemView); 89 | usernameText = itemView.findViewById(R.id.user_name_text); 90 | lastMessageText = itemView.findViewById(R.id.last_message_text); 91 | lastMessageTime = itemView.findViewById(R.id.last_message_time_text); 92 | profilePic = itemView.findViewById(R.id.profile_pic_image_view); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/adapter/SearchUserRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.recyclerview.widget.RecyclerView; 14 | 15 | import com.example.easychat.ChatActivity; 16 | import com.example.easychat.R; 17 | import com.example.easychat.model.UserModel; 18 | import com.example.easychat.utils.AndroidUtil; 19 | import com.example.easychat.utils.FirebaseUtil; 20 | import com.firebase.ui.firestore.FirestoreRecyclerAdapter; 21 | import com.firebase.ui.firestore.FirestoreRecyclerOptions; 22 | 23 | public class SearchUserRecyclerAdapter extends FirestoreRecyclerAdapter { 24 | 25 | Context context; 26 | 27 | public SearchUserRecyclerAdapter(@NonNull FirestoreRecyclerOptions options,Context context) { 28 | super(options); 29 | this.context = context; 30 | } 31 | 32 | @Override 33 | protected void onBindViewHolder(@NonNull UserModelViewHolder holder, int position, @NonNull UserModel model) { 34 | holder.usernameText.setText(model.getUsername()); 35 | holder.phoneText.setText(model.getPhone()); 36 | if(model.getUserId().equals(FirebaseUtil.currentUserId())){ 37 | holder.usernameText.setText(model.getUsername()+" (Me)"); 38 | } 39 | 40 | FirebaseUtil.getOtherProfilePicStorageRef(model.getUserId()).getDownloadUrl() 41 | .addOnCompleteListener(t -> { 42 | if(t.isSuccessful()){ 43 | Uri uri = t.getResult(); 44 | AndroidUtil.setProfilePic(context,uri,holder.profilePic); 45 | } 46 | }); 47 | 48 | holder.itemView.setOnClickListener(v -> { 49 | //navigate to chat activity 50 | Intent intent = new Intent(context, ChatActivity.class); 51 | AndroidUtil.passUserModelAsIntent(intent,model); 52 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 53 | context.startActivity(intent); 54 | }); 55 | } 56 | 57 | @NonNull 58 | @Override 59 | public UserModelViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 60 | View view = LayoutInflater.from(context).inflate(R.layout.search_user_recycler_row,parent,false); 61 | return new UserModelViewHolder(view); 62 | } 63 | 64 | class UserModelViewHolder extends RecyclerView.ViewHolder{ 65 | TextView usernameText; 66 | TextView phoneText; 67 | ImageView profilePic; 68 | 69 | public UserModelViewHolder(@NonNull View itemView) { 70 | super(itemView); 71 | usernameText = itemView.findViewById(R.id.user_name_text); 72 | phoneText = itemView.findViewById(R.id.phone_text); 73 | profilePic = itemView.findViewById(R.id.profile_pic_image_view); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/model/ChatMessageModel.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat.model; 2 | 3 | import com.google.firebase.Timestamp; 4 | 5 | public class ChatMessageModel { 6 | private String message; 7 | private String senderId; 8 | private Timestamp timestamp; 9 | 10 | public ChatMessageModel() { 11 | } 12 | 13 | public ChatMessageModel(String message, String senderId, Timestamp timestamp) { 14 | this.message = message; 15 | this.senderId = senderId; 16 | this.timestamp = timestamp; 17 | } 18 | 19 | public String getMessage() { 20 | return message; 21 | } 22 | 23 | public void setMessage(String message) { 24 | this.message = message; 25 | } 26 | 27 | public String getSenderId() { 28 | return senderId; 29 | } 30 | 31 | public void setSenderId(String senderId) { 32 | this.senderId = senderId; 33 | } 34 | 35 | public Timestamp getTimestamp() { 36 | return timestamp; 37 | } 38 | 39 | public void setTimestamp(Timestamp timestamp) { 40 | this.timestamp = timestamp; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/model/ChatroomModel.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat.model; 2 | 3 | import com.google.firebase.Timestamp; 4 | 5 | import java.util.List; 6 | 7 | public class ChatroomModel { 8 | String chatroomId; 9 | List userIds; 10 | Timestamp lastMessageTimestamp; 11 | String lastMessageSenderId; 12 | String lastMessage; 13 | 14 | public ChatroomModel() { 15 | } 16 | 17 | public ChatroomModel(String chatroomId, List userIds, Timestamp lastMessageTimestamp, String lastMessageSenderId) { 18 | this.chatroomId = chatroomId; 19 | this.userIds = userIds; 20 | this.lastMessageTimestamp = lastMessageTimestamp; 21 | this.lastMessageSenderId = lastMessageSenderId; 22 | } 23 | 24 | public String getChatroomId() { 25 | return chatroomId; 26 | } 27 | 28 | public void setChatroomId(String chatroomId) { 29 | this.chatroomId = chatroomId; 30 | } 31 | 32 | public List getUserIds() { 33 | return userIds; 34 | } 35 | 36 | public void setUserIds(List userIds) { 37 | this.userIds = userIds; 38 | } 39 | 40 | public Timestamp getLastMessageTimestamp() { 41 | return lastMessageTimestamp; 42 | } 43 | 44 | public void setLastMessageTimestamp(Timestamp lastMessageTimestamp) { 45 | this.lastMessageTimestamp = lastMessageTimestamp; 46 | } 47 | 48 | public String getLastMessageSenderId() { 49 | return lastMessageSenderId; 50 | } 51 | 52 | public void setLastMessageSenderId(String lastMessageSenderId) { 53 | this.lastMessageSenderId = lastMessageSenderId; 54 | } 55 | 56 | public String getLastMessage() { 57 | return lastMessage; 58 | } 59 | 60 | public void setLastMessage(String lastMessage) { 61 | this.lastMessage = lastMessage; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/model/UserModel.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat.model; 2 | 3 | import com.google.firebase.Timestamp; 4 | 5 | public class UserModel { 6 | private String phone; 7 | private String username; 8 | private Timestamp createdTimestamp; 9 | private String userId; 10 | private String fcmToken; 11 | 12 | public UserModel() { 13 | } 14 | 15 | public UserModel(String phone, String username, Timestamp createdTimestamp,String userId) { 16 | this.phone = phone; 17 | this.username = username; 18 | this.createdTimestamp = createdTimestamp; 19 | this.userId = userId; 20 | } 21 | 22 | public String getPhone() { 23 | return phone; 24 | } 25 | 26 | public void setPhone(String phone) { 27 | this.phone = phone; 28 | } 29 | 30 | public String getUsername() { 31 | return username; 32 | } 33 | 34 | public void setUsername(String username) { 35 | this.username = username; 36 | } 37 | 38 | public Timestamp getCreatedTimestamp() { 39 | return createdTimestamp; 40 | } 41 | 42 | public void setCreatedTimestamp(Timestamp createdTimestamp) { 43 | this.createdTimestamp = createdTimestamp; 44 | } 45 | 46 | public String getUserId() { 47 | return userId; 48 | } 49 | 50 | public void setUserId(String userId) { 51 | this.userId = userId; 52 | } 53 | 54 | public String getFcmToken() { 55 | return fcmToken; 56 | } 57 | 58 | public void setFcmToken(String fcmToken) { 59 | this.fcmToken = fcmToken; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/utils/AndroidUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat.utils; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.widget.ImageView; 7 | import android.widget.Toast; 8 | 9 | import com.bumptech.glide.Glide; 10 | import com.bumptech.glide.request.RequestOptions; 11 | import com.example.easychat.model.UserModel; 12 | import com.google.firebase.firestore.auth.User; 13 | 14 | public class AndroidUtil { 15 | 16 | public static void showToast(Context context,String message){ 17 | Toast.makeText(context,message,Toast.LENGTH_LONG).show(); 18 | } 19 | 20 | public static void passUserModelAsIntent(Intent intent, UserModel model){ 21 | intent.putExtra("username",model.getUsername()); 22 | intent.putExtra("phone",model.getPhone()); 23 | intent.putExtra("userId",model.getUserId()); 24 | intent.putExtra("fcmToken",model.getFcmToken()); 25 | 26 | } 27 | 28 | public static UserModel getUserModelFromIntent(Intent intent){ 29 | UserModel userModel = new UserModel(); 30 | userModel.setUsername(intent.getStringExtra("username")); 31 | userModel.setPhone(intent.getStringExtra("phone")); 32 | userModel.setUserId(intent.getStringExtra("userId")); 33 | userModel.setFcmToken(intent.getStringExtra("fcmToken")); 34 | return userModel; 35 | } 36 | 37 | public static void setProfilePic(Context context, Uri imageUri, ImageView imageView){ 38 | Glide.with(context).load(imageUri).apply(RequestOptions.circleCropTransform()).into(imageView); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/easychat/utils/FirebaseUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.easychat.utils; 2 | 3 | import com.google.firebase.Timestamp; 4 | import com.google.firebase.auth.FirebaseAuth; 5 | import com.google.firebase.firestore.CollectionReference; 6 | import com.google.firebase.firestore.DocumentReference; 7 | import com.google.firebase.firestore.FirebaseFirestore; 8 | import com.google.firebase.storage.FirebaseStorage; 9 | import com.google.firebase.storage.StorageReference; 10 | 11 | import java.text.SimpleDateFormat; 12 | import java.util.List; 13 | 14 | public class FirebaseUtil { 15 | 16 | public static String currentUserId(){ 17 | return FirebaseAuth.getInstance().getUid(); 18 | } 19 | 20 | public static boolean isLoggedIn(){ 21 | if(currentUserId()!=null){ 22 | return true; 23 | } 24 | return false; 25 | } 26 | 27 | public static DocumentReference currentUserDetails(){ 28 | return FirebaseFirestore.getInstance().collection("users").document(currentUserId()); 29 | } 30 | 31 | public static CollectionReference allUserCollectionReference(){ 32 | return FirebaseFirestore.getInstance().collection("users"); 33 | } 34 | 35 | public static DocumentReference getChatroomReference(String chatroomId){ 36 | return FirebaseFirestore.getInstance().collection("chatrooms").document(chatroomId); 37 | } 38 | 39 | public static CollectionReference getChatroomMessageReference(String chatroomId){ 40 | return getChatroomReference(chatroomId).collection("chats"); 41 | } 42 | 43 | public static String getChatroomId(String userId1,String userId2){ 44 | if(userId1.hashCode() userIds){ 56 | if(userIds.get(0).equals(FirebaseUtil.currentUserId())){ 57 | return allUserCollectionReference().document(userIds.get(1)); 58 | }else{ 59 | return allUserCollectionReference().document(userIds.get(0)); 60 | } 61 | } 62 | 63 | public static String timestampToString(Timestamp timestamp){ 64 | return new SimpleDateFormat("HH:MM").format(timestamp.toDate()); 65 | } 66 | 67 | public static void logout(){ 68 | FirebaseAuth.getInstance().signOut(); 69 | } 70 | 71 | public static StorageReference getCurrentProfilePicStorageRef(){ 72 | return FirebaseStorage.getInstance().getReference().child("profile_pic") 73 | .child(FirebaseUtil.currentUserId()); 74 | } 75 | 76 | public static StorageReference getOtherProfilePicStorageRef(String otherUserId){ 77 | return FirebaseStorage.getInstance().getReference().child("profile_pic") 78 | .child(otherUserId); 79 | } 80 | 81 | 82 | } 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_rounded_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/chat_icon.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/circular_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/edit_text_rounded_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_back.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_search.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_send.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bimalkaf/Android_Chat_Application/88e4537ed17e5a812a403b2c30849a35481b49b2/app/src/main/res/drawable/launcher_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/otp_icon.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/person_icon.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/phone_icon.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_chat.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 28 | 29 | 36 | 37 | 45 | 46 | 47 | 48 | 54 | 55 | 61 | 62 | 76 | 77 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login_otp.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 24 | 25 | 32 | 33 | 39 | 40 | 47 | 48 | 54 | 55 | 62 | 63 | 64 | 65 | 72 | 73 | 82 | 83 | 89 | 90 | 91 | 92 | 104 | 105 | 106 | 107 | 113 |