├── CHANGELOG.md ├── LICENSE ├── PRIVACY_POLICY.md ├── README.md ├── TERMS_AND_CONDITIONS.md ├── THIRD_PARTY_NOTICES.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── github │ │ └── h01d │ │ └── chatapp │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── h01d │ │ │ └── chatapp │ │ │ ├── MainActivity.java │ │ │ ├── activities │ │ │ ├── ChatActivity.java │ │ │ ├── FullScreenActivity.java │ │ │ ├── ProfileActivity.java │ │ │ ├── UsersActivity.java │ │ │ └── WelcomeActivity.java │ │ │ ├── adapters │ │ │ └── MessageAdapter.java │ │ │ ├── fragments │ │ │ ├── ChatFragment.java │ │ │ ├── FriendsFragment.java │ │ │ └── RequestsFragment.java │ │ │ ├── holders │ │ │ ├── ChatHolder.java │ │ │ ├── FriendHolder.java │ │ │ ├── MessageHolder.java │ │ │ ├── RequestHolder.java │ │ │ └── UserHolder.java │ │ │ ├── models │ │ │ ├── Chat.java │ │ │ ├── Friend.java │ │ │ ├── Message.java │ │ │ ├── Request.java │ │ │ └── User.java │ │ │ └── utils │ │ │ ├── Capabilities.java │ │ │ ├── FirebaseMessagingService.java │ │ │ └── TouchImageView.java │ └── res │ │ ├── drawable │ │ ├── circle.xml │ │ ├── corner.xml │ │ ├── ic_add_white_24dp.xml │ │ ├── ic_block_white_24dp.xml │ │ ├── ic_cancel_white_24dp.xml │ │ ├── ic_edit_white_24dp.xml │ │ ├── ic_email_black_24dp.xml │ │ ├── ic_filter_hdr_white_24dp.xml │ │ ├── ic_group_white_24dp.xml │ │ ├── ic_image_black_24dp.xml │ │ ├── ic_image_white_24dp.xml │ │ ├── ic_lock_black_24dp.xml │ │ ├── ic_message_white_24dp.xml │ │ ├── ic_person_add_white_24dp.xml │ │ ├── ic_person_black_24dp.xml │ │ ├── ic_person_white_24dp.xml │ │ ├── ic_remove_circle_white_24dp.xml │ │ ├── ic_send_black_24dp.xml │ │ ├── logo.png │ │ ├── logo_cover.png │ │ ├── logo_shadow.png │ │ ├── message_background.xml │ │ └── user.png │ │ ├── layout │ │ ├── activity_chat.xml │ │ ├── activity_full_screen.xml │ │ ├── activity_main.xml │ │ ├── activity_profile.xml │ │ ├── activity_users.xml │ │ ├── activity_welcome.xml │ │ ├── app_bar.xml │ │ ├── chat_bar.xml │ │ ├── fragment_chat.xml │ │ ├── fragment_friends.xml │ │ ├── fragment_request.xml │ │ ├── message.xml │ │ ├── status_dialog.xml │ │ └── user.xml │ │ ├── menu │ │ └── main_menu.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 │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── github │ └── h01d │ └── chatapp │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── index.js └── settings.gradle /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## Version 1.1 _(13/03/2018)_ 5 | 6 | - Added option to change Cover Image in Profile. 7 | - Added Notification when someone accepts Friend Request. 8 | - Added image sending in Chat. 9 | - Added image full screen option in profile. 10 | - Added Menu with View Profile, Accept Request in Requests. 11 | - Added Ripple Effect and Deviders in Lists of Request/Chat/Friends Tab as well as Users. 12 | 13 | - Fixed bug when minimizing Chat would not update messages after re-opening. 14 | - Fixed bug where Android Versions with API 26+ would not receive Notifications. 15 | 16 | - Changed Friends Tab and now friends are sorted by date. 17 | - Changed logo of Friend Request notification. 18 | - Changed Chat overall typing experiance and now will not drop keyboard after sending message. 19 | - Changed Login/Registers boxes to maxLine=1 so they can work properly with enter. 20 | 21 | - Removed enter-key send message. 22 | 23 | ## Version 1.0 _(26/02/2018)_ 24 | 25 | - Initial Release. 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Raf. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | *Last Revised: 27/02/2018* 4 | 5 | We built the ChatApp app as an Open Source app. This SERVICE is provided at no cost and is intended for use as is. 6 | 7 | This page is used to inform Service users regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service. 8 | 9 | If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy. 10 | 11 | The terms used in this Privacy Policy have the same meanings as in our [Terms and Conditions](TERMS_AND_CONDITIONS.md), which is accessible at ChatApp unless otherwise defined in this Privacy Policy. 12 | 13 | ## Information Collection and Use 14 | 15 | For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information, including but not limited to Email, Username, Password, Profile Picture. The information that we request are retained on Google Servers and accessed only but us. 16 | 17 | The app does use third party services that may collect information used to identify you. 18 | 19 | Link to privacy policy of third party service providers used by the app 20 | 21 | - [Google Play Services](https://www.google.com/policies/privacy/) 22 | - [Firebase](https://www.firebase.com/terms/terms-of-service.html) 23 | 24 | Other third party libraries used by the app 25 | 26 | - [Third Party Notices](THIRD_PARTY_NOTICES.md) 27 | 28 | ## Crash Reporting 29 | 30 | We want to inform you that whenever you use our Service, in a case of an error in the app we collect data and information (through third party products) so we can fix or avoid it. This report may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing our Service, the time and date of your use of the Service, and other statistics. 31 | 32 | ## Service Providers 33 | 34 | We may employ third-party companies and individuals due to the following reasons: 35 | 36 | - To facilitate our Service; 37 | - To provide the Service on our behalf; 38 | - To perform Service-related services; or 39 | - To assist us in analyzing how our Service is used. 40 | 41 | We want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 42 | 43 | ## Security 44 | 45 | We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security. 46 | 47 | ## Privacy 48 | 49 | These Services do not address anyone under the age of 13. We do not knowingly collect personally identifiable information from children under 13. In the case we discover that a child under 13 has provided me with personal information, we immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact us so that we will be able to do necessary actions. 50 | 51 | ## Changes to this Privacy Policy 52 | 53 | We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page. 54 | 55 | ## Contact Us 56 | 57 | If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at rafaellos.g9@gmail.com. 58 | 59 | *This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.firebaseapp.com), Raf.* 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatApp v1.1 2 | 3 | ChatApp is a project that I started to improve my Android Development knowledge. Later on I have decided to make it public, open source and publish it on Play Store. 4 | 5 | It uses [Firebase's](https://firebase.google.com) Authentication/Database/Storage/Messaging/CrashReporting libraries for it's implementation and several other libraries that they are listed on [Third Party Notices](THIRD_PARTY_NOTICES.md). 6 | 7 | Make sure you read App's [Privacy Policy](PRIVACY_POLICY.md) and [Terms and Conditions](TERMS_AND_CONDITIONS.md) before using it. 8 | 9 | _Based on [Lapit Chat](https://github.com/akshayejh/Lapit---Android-Firebase-Chat-App) Youtube Series which can be found [here](https://www.youtube.com/playlist?list=PLGCjwl1RrtcQ3o2jmZtwu2wXEA4OIIq53)._ 10 | 11 | **DOWNLOAD LINK:** [Play Store](https://play.google.com/store/apps/details?id=com.github.h01d.chatapp) 12 | 13 | ## Preview 14 | 15 | ![login](https://i.imgur.com/eXlXGiX.png) 16 | 17 | _Browse all preview pictures [here](https://imgur.com/a/HQhKw)._ 18 | 19 | ## Features 20 | 21 | - Messaging 22 | - Send and Receive messages with users 23 | - Send pictures 24 | - Lists 25 | - List with your Requests 26 | - List with your Messages 27 | - List with your Friends 28 | - List with all Users 29 | - Friends 30 | - Accept or Remove Friends 31 | - Requests 32 | - Send or Cancel Friend Request to users 33 | - Profile 34 | - Update your Profile Picture 35 | - Update your Status 36 | - Update your Cover Picture 37 | - View other users profile 38 | - Notifications 39 | - Notification when you have a new message 40 | - Notification when you have a new friend request 41 | - Notification when someone accepts your request 42 | 43 | **Upcoming** 44 | 45 | - Blocking 46 | - Block user from sending messages 47 | 48 | ## Changelog 49 | 50 | Read all Changelog [HERE](CHANGELOG.md). 51 | 52 | ## Installation 53 | 54 | *Setting up project* 55 | 56 | - Download Project 57 | - Create a new [Firebase](https://firebase.google.com) Project in console 58 | - Connect project with Firebase `(Tools/Firebase)` in Android Studio 59 | - Generate, download, paste `google-services.json` into the project 60 | 61 | *Setting up notifications back-end* 62 | 63 | - Create a folder on your Desktop and open it 64 | - Start CMD (for Windows) or Terminal (for MacOS/Linux) 65 | - Login on Firebase CLI using `firebase login` 66 | - Type `firebase init`, select `Functions` using the `Space` key and hit `Enter` 67 | - Select your App, then `javascript`, `N` on ESLint, and `Y`on dependendcies with npm. 68 | - Navigate `functions` folder and replace `index.js` with [this](note.js) 69 | - Type `firebase deploy` and you are all set 70 | 71 | **NOTE:** make sure you read project's [LICENSE](LICENSE) before start playing with it. 72 | 73 | ## License 74 | 75 | ``` 76 | Copyright 2018 Raf. 77 | 78 | Licensed under the Apache License, Version 2.0 (the "License"); 79 | you may not use this file except in compliance with the License. 80 | You may obtain a copy of the License at 81 | 82 | http://www.apache.org/licenses/LICENSE-2.0 83 | 84 | Unless required by applicable law or agreed to in writing, software 85 | distributed under the License is distributed on an "AS IS" BASIS, 86 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 87 | See the License for the specific language governing permissions and 88 | limitations under the License. 89 | -------------------------------------------------------------------------------- /TERMS_AND_CONDITIONS.md: -------------------------------------------------------------------------------- 1 | # Terms and conditions 2 | 3 | *Last Revised: 27/02/2018* 4 | 5 | These terms and conditions ("Terms", "Agreement") are an agreement between ChatApp Operator ("ChatApp Operator", "us", "we" or "our") and you ("User", "you" or "your"). This Agreement sets forth the general terms and conditions of your use of the ChatApp application and any of its products or services (collectively, "Application" or "Services"). 6 | 7 | ## Accounts 8 | 9 | You must be at least 13 years of age to use this Application. By using this Application and by agreeing to this Agreement you warrant and represent that you are at least 13 years of age. If you create an account on the Application, you are responsible for maintaining the security of your account and you are fully responsible for all activities that occur under the account and any other actions taken in connection with it. Providing false information of any kind may result in the termination of your account. You must immediately notify us of any unauthorized uses of your account or any other breaches of security. We will not be liable for any acts or omissions by you, including any damages of any kind incurred as a result of such acts or omissions. We may suspend, disable, or delete your account (or any part thereof) if we determine that you have violated any provision of this Agreement or that your conduct or content would tend to damage our reputation and goodwill. If we delete your account for the foregoing reasons, you may not re-register for the our Services. 10 | 11 | ## User content 12 | 13 | We do not own any data, information or material ("Content") that you submit on the Application in the course of using the Service. You shall have sole responsibility for the accuracy, quality, integrity, legality, reliability, appropriateness, and intellectual property ownership or right to use of all submitted Content. We may, but have no obligation to, monitor Content on the Application submitted or created using our Services by you. Unless specifically permitted by you, your use of the Application does not grant us the license to use, reproduce, adapt, modify, publish or distribute the Content created by you or stored in your user account for commercial, marketing or any similar purpose. But you grant us permission to access, copy, distribute, store, transmit, reformat, display and perform the Content of your user account solely as required for the purpose of providing the Services to you. Without limiting any of those representations or warranties, we have the right, though not the obligation, to, in our own sole discretion, refuse or remove any Content that, in our reasonable opinion, violates any of our policies or is in any way harmful or objectionable. 14 | 15 | ## Changes 16 | 17 | We reserve the right to modify this Agreement or its policies relating the Application or Services at any time, effective upon posting of an updated version of this Agreement on the Website. When we do we will revise the updated date at the top of this page. Continued use of the Website after any such changes shall constitute your consent to such changes. 18 | 19 | ## Acceptance of these terms 20 | 21 | You acknowledge that you have read this Agreement and agree to all its terms and conditions. By using the Application or its Services you agree to be bound by this Agreement. If you do not agree to abide by the terms of this Agreement, you are not authorized to use or access the Application and its Services. 22 | 23 | ## Contact Us 24 | 25 | If you have any questions or suggestions about our Terms and Conditions, do not hesitate to contact us at rafaellos.g9@gmail.com. 26 | 27 | *This terms and conditions page was generated by [Website Policies](https://www.websitepolicies.com) and modified by Raf.* 28 | -------------------------------------------------------------------------------- /THIRD_PARTY_NOTICES.md: -------------------------------------------------------------------------------- 1 | # Third Party Notices 2 | 3 | ## **[Picasso](https://github.com/square/picasso)** 4 | 5 | Copyright 2013 Square, Inc. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | ## **[OkHttp](https://github.com/square/okhttp)** 20 | 21 | Licensed under the Apache License, Version 2.0 (the "License"); 22 | you may not use this file except in compliance with the License. 23 | You may obtain a copy of the License at 24 | 25 | http://www.apache.org/licenses/LICENSE-2.0 26 | 27 | Unless required by applicable law or agreed to in writing, software 28 | distributed under the License is distributed on an "AS IS" BASIS, 29 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30 | See the License for the specific language governing permissions and 31 | limitations under the License. 32 | 33 | ## **[CircleImageView](https://github.com/hdodenhof/CircleImageView)** 34 | 35 | Copyright 2014 - 2017 Henning Dodenhof 36 | 37 | Licensed under the Apache License, Version 2.0 (the "License"); 38 | you may not use this file except in compliance with the License. 39 | You may obtain a copy of the License at 40 | 41 | http://www.apache.org/licenses/LICENSE-2.0 42 | 43 | Unless required by applicable law or agreed to in writing, software 44 | distributed under the License is distributed on an "AS IS" BASIS, 45 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 46 | See the License for the specific language governing permissions and 47 | limitations under the License. 48 | 49 | ## **[AndroidSlidingUpPanel](https://github.com/umano/AndroidSlidingUpPanel)** 50 | 51 | Copyright 2015 Anton Lopyrev 52 | 53 | Licensed under the Apache License, Version 2.0 (the "License"); 54 | you may not use this file except in compliance with the License. 55 | You may obtain a copy of the License at 56 | 57 | http://www.apache.org/licenses/LICENSE-2.0 58 | 59 | Unless required by applicable law or agreed to in writing, software 60 | distributed under the License is distributed on an "AS IS" BASIS, 61 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 62 | See the License for the specific language governing permissions and 63 | limitations under the License. 64 | 65 | ## **[DiagonalLayout](https://github.com/florent37/DiagonalLayout)** 66 | 67 | Copyright 2016 florent37, Inc. 68 | 69 | Licensed under the Apache License, Version 2.0 (the "License"); 70 | you may not use this file except in compliance with the License. 71 | You may obtain a copy of the License at 72 | 73 | http://www.apache.org/licenses/LICENSE-2.0 74 | 75 | Unless required by applicable law or agreed to in writing, software 76 | distributed under the License is distributed on an "AS IS" BASIS, 77 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 78 | See the License for the specific language governing permissions and 79 | limitations under the License. 80 | 81 | ## **[KenBurnsView](https://github.com/flavioarfaria/KenBurnsView)** 82 | 83 | Copyright {yyyy} {name of copyright owner} 84 | 85 | Licensed under the Apache License, Version 2.0 (the "License"); 86 | you may not use this file except in compliance with the License. 87 | You may obtain a copy of the License at 88 | 89 | http://www.apache.org/licenses/LICENSE-2.0 90 | 91 | Unless required by applicable law or agreed to in writing, software 92 | distributed under the License is distributed on an "AS IS" BASIS, 93 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 94 | See the License for the specific language governing permissions and 95 | limitations under the License. 96 | 97 | ## **[SmartTabLayout](https://github.com/ogaclejapan/SmartTabLayout)** 98 | 99 | Copyright (C) 2015 ogaclejapan 100 | Copyright (C) 2013 The Android Open Source Project 101 | 102 | Licensed under the Apache License, Version 2.0 (the "License"); 103 | you may not use this file except in compliance with the License. 104 | You may obtain a copy of the License at 105 | 106 | http://www.apache.org/licenses/LICENSE-2.0 107 | 108 | Unless required by applicable law or agreed to in writing, software 109 | distributed under the License is distributed on an "AS IS" BASIS, 110 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 111 | See the License for the specific language governing permissions and 112 | limitations under the License. 113 | 114 | ## **[FABsMenu](https://github.com/jahirfiquitiva/FABsMenu)** 115 | 116 | Copyright (c) 2014 Jerzy Chalupski 117 | 118 | Licensed under the Apache License, Version 2.0 (the "License"); 119 | you may not use this file except in compliance with the License. 120 | You may obtain a copy of the License at 121 | 122 | http://www.apache.org/licenses/LICENSE-2.0 123 | 124 | Unless required by applicable law or agreed to in writing, software 125 | distributed under the License is distributed on an "AS IS" BASIS, 126 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 127 | See the License for the specific language governing permissions and 128 | limitations under the License. 129 | 130 | ## **[android-process-button](https://github.com/dmytrodanylyk/android-process-button)** 131 | 132 | The MIT License (MIT) 133 | 134 | Copyright (c) 2014 Danylyk Dmytro 135 | 136 | Permission is hereby granted, free of charge, to any person obtaining a copy 137 | of this software and associated documentation files (the "Software"), to deal 138 | in the Software without restriction, including without limitation the rights 139 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 140 | copies of the Software, and to permit persons to whom the Software is 141 | furnished to do so, subject to the following conditions: 142 | 143 | The above copyright notice and this permission notice shall be included in all 144 | copies or substantial portions of the Software. 145 | 146 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 147 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 148 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 149 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 150 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 151 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 152 | SOFTWARE. 153 | 154 | ## **[CircularAnim](https://github.com/XunMengWinter/CircularAnim)** 155 | 156 | Library under no license. 157 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | googl -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | /** 4 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 5 | * Licensed under Apache License 2.0 6 | * 7 | * @author Raf (https://github.com/h01d) 8 | * @version 1.1 9 | * @since 27/02/2018 10 | */ 11 | 12 | android { 13 | compileSdkVersion 27 14 | defaultConfig { 15 | applicationId "com.github.h01d.chatapp" 16 | minSdkVersion 21 17 | targetSdkVersion 27 18 | versionCode 2 19 | versionName "1.1" 20 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 21 | } 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation 'com.android.support:appcompat-v7:27.0.2' 33 | implementation 'com.android.support:support-v4:27.0.2' 34 | implementation 'com.android.support:design:27.0.2' 35 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 36 | 37 | implementation 'com.google.firebase:firebase-auth:11.8.0' 38 | implementation 'com.google.firebase:firebase-database:11.8.0' 39 | implementation 'com.google.firebase:firebase-storage:11.8.0' 40 | implementation 'com.firebaseui:firebase-ui-database:3.1.0' 41 | implementation 'com.google.firebase:firebase-messaging:11.8.0' 42 | implementation 'com.google.firebase:firebase-crash:11.8.0' 43 | 44 | testImplementation 'junit:junit:4.12' 45 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 46 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 47 | 48 | compile 'com.squareup.picasso:picasso:2.5.2' 49 | //https://github.com/square/picasso (Apache License 2.0) 50 | compile 'com.squareup.okhttp:okhttp:2.5.0' 51 | //https://github.com/square/okhttp (Apache License 2.0) 52 | compile 'de.hdodenhof:circleimageview:2.2.0' 53 | //https://github.com/hdodenhof/CircleImageView (Apache License 2.0) 54 | compile 'com.sothree.slidinguppanel:library:3.4.0' 55 | //https://github.com/umano/AndroidSlidingUpPanel (Apache License 2.0) 56 | compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4' 57 | //https://github.com/dmytrodanylyk/android-process-button (MIT) 58 | compile 'com.github.XunMengWinter:CircularAnim:0.3.4' 59 | //https://github.com/XunMengWinter/CircularAnim (No license) 60 | compile 'com.github.florent37:diagonallayout:1.0.8' 61 | //https://github.com/florent37/DiagonalLayout (Apache License 2.0) 62 | compile 'com.flaviofaria:kenburnsview:1.0.7' 63 | //https://github.com/flavioarfaria/KenBurnsView (Apache License 2.0) 64 | compile 'com.ogaclejapan.smarttablayout:library:1.6.1@aar' 65 | //https://github.com/ogaclejapan/SmartTabLayout (Apache License 2.0) 66 | compile 'com.ogaclejapan.smarttablayout:utils-v4:1.6.1@aar' 67 | //https://github.com/ogaclejapan/SmartTabLayout (Apache License 2.0) 68 | compile 'me.jahirfiquitiva:FABsMenu:1.1.1' 69 | //https://github.com/jahirfiquitiva/FABsMenu (Apache License 2.0) 70 | } 71 | 72 | 73 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /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/github/h01d/chatapp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.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 | { 20 | @Test 21 | public void useAppContext() throws Exception 22 | { 23 | // Context of the app under test. 24 | Context appContext = InstrumentationRegistry.getTargetContext(); 25 | 26 | assertEquals("com.example.kappa.chatapp", appContext.getPackageName()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 10 | 12 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 61 | 62 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamraf/ChatApp/6d10109408041905ef344582b7894e8a509e33f7/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.graphics.Color; 6 | import android.net.Uri; 7 | import android.support.v4.view.ViewPager; 8 | import android.support.v7.app.AlertDialog; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.os.Bundle; 11 | import android.support.v7.widget.Toolbar; 12 | import android.view.Menu; 13 | import android.view.MenuItem; 14 | 15 | import com.github.h01d.chatapp.activities.ProfileActivity; 16 | import com.github.h01d.chatapp.activities.UsersActivity; 17 | import com.github.h01d.chatapp.activities.WelcomeActivity; 18 | import com.github.h01d.chatapp.fragments.ChatFragment; 19 | import com.github.h01d.chatapp.fragments.FriendsFragment; 20 | import com.github.h01d.chatapp.fragments.RequestsFragment; 21 | import com.google.firebase.auth.FirebaseAuth; 22 | import com.google.firebase.auth.FirebaseUser; 23 | import com.google.firebase.database.FirebaseDatabase; 24 | import com.google.firebase.database.ServerValue; 25 | import com.ogaclejapan.smarttablayout.SmartTabLayout; 26 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter; 27 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems; 28 | 29 | /** 30 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 31 | * Licensed under Apache License 2.0 32 | * 33 | * @author Raf (https://github.com/h01d) 34 | * @version 1.1 35 | * @since 27/02/2018 36 | */ 37 | 38 | public class MainActivity extends AppCompatActivity 39 | { 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) 42 | { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_main); 45 | 46 | // Action bar related 47 | 48 | Toolbar toolbar = findViewById(R.id.main_app_bar); 49 | toolbar.setTitleTextColor(Color.WHITE); 50 | setSupportActionBar(toolbar); 51 | getSupportActionBar().setTitle("ChatApp"); 52 | 53 | // Fragments handler using SmartTabLayout 54 | 55 | FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter( 56 | getSupportFragmentManager(), FragmentPagerItems.with(this) 57 | .add("Requests", RequestsFragment.class) 58 | .add("Chat", ChatFragment.class) 59 | .add("Friends", FriendsFragment.class) 60 | .create()); 61 | 62 | ViewPager viewPager = findViewById(R.id.viewpager); 63 | viewPager.setAdapter(adapter); 64 | viewPager.setCurrentItem(1); 65 | 66 | SmartTabLayout viewPagerTab = findViewById(R.id.viewpagertab); 67 | viewPagerTab.setViewPager(viewPager); 68 | } 69 | 70 | @Override 71 | public void onStart() 72 | { 73 | super.onStart(); 74 | 75 | if(FirebaseAuth.getInstance().getCurrentUser() == null) 76 | { 77 | // If no logged in user send them to login/register 78 | 79 | Intent welcomeIntent = new Intent(MainActivity.this, WelcomeActivity.class); 80 | startActivity(welcomeIntent); 81 | finish(); 82 | } 83 | } 84 | 85 | @Override 86 | protected void onResume() 87 | { 88 | super.onResume(); 89 | 90 | FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser(); 91 | 92 | if(currentUser != null) 93 | { 94 | FirebaseDatabase.getInstance().getReference().child("Users").child(currentUser.getUid()).child("online").setValue("true"); 95 | } 96 | } 97 | 98 | @Override 99 | protected void onPause() 100 | { 101 | super.onPause(); 102 | 103 | FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser(); 104 | 105 | if(currentUser != null) 106 | { 107 | FirebaseDatabase.getInstance().getReference().child("Users").child(currentUser.getUid()).child("online").setValue(ServerValue.TIMESTAMP); 108 | } 109 | } 110 | 111 | @Override 112 | public void onBackPressed() 113 | { 114 | AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); 115 | builder.setTitle("Exit"); 116 | builder.setMessage("Are you sure you want to close the application?"); 117 | builder.setPositiveButton("YES", new DialogInterface.OnClickListener() 118 | { 119 | public void onClick(DialogInterface dialog, int id) 120 | { 121 | finish(); 122 | Intent intent = new Intent(Intent.ACTION_MAIN); 123 | intent.addCategory(Intent.CATEGORY_HOME); 124 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 125 | startActivity(intent); 126 | } 127 | }); 128 | builder.setNegativeButton("NO", new DialogInterface.OnClickListener() 129 | { 130 | public void onClick(DialogInterface dialog, int id) 131 | { 132 | dialog.dismiss(); 133 | } 134 | }); 135 | AlertDialog dialog = builder.create(); 136 | dialog.show(); 137 | } 138 | 139 | @Override 140 | public boolean onCreateOptionsMenu(Menu menu) 141 | { 142 | super.onCreateOptionsMenu(menu); 143 | 144 | getMenuInflater().inflate(R.menu.main_menu, menu); 145 | return true; 146 | } 147 | 148 | @Override 149 | public boolean onOptionsItemSelected(MenuItem item) 150 | { 151 | super.onOptionsItemSelected(item); 152 | 153 | switch(item.getItemId()) 154 | { 155 | case R.id.menuLogout: 156 | AlertDialog.Builder logoutBuilder = new AlertDialog.Builder(MainActivity.this); 157 | logoutBuilder.setTitle("Logout"); 158 | logoutBuilder.setMessage("Are you sure you want to logout?"); 159 | logoutBuilder.setPositiveButton("YES", new DialogInterface.OnClickListener() 160 | { 161 | public void onClick(DialogInterface dialog, int id) 162 | { 163 | FirebaseDatabase.getInstance().getReference().child("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("online").setValue(ServerValue.TIMESTAMP); 164 | 165 | FirebaseAuth.getInstance().signOut(); 166 | 167 | Intent welcomeIntent = new Intent(MainActivity.this, WelcomeActivity.class); 168 | startActivity(welcomeIntent); 169 | finish(); 170 | } 171 | }); 172 | logoutBuilder.setNegativeButton("NO", new DialogInterface.OnClickListener() 173 | { 174 | public void onClick(DialogInterface dialog, int id) 175 | { 176 | dialog.dismiss(); 177 | } 178 | }); 179 | AlertDialog logoutDialog = logoutBuilder.create(); 180 | logoutDialog.show(); 181 | return true; 182 | case R.id.menuChangelog: 183 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/h01d/ChatApp/blob/master/CHANGELOG.md"))); 184 | return true; 185 | case R.id.menuAbout: 186 | AlertDialog.Builder aboutBuilder = new AlertDialog.Builder(MainActivity.this); 187 | aboutBuilder.setTitle("ChatApp v1.1"); 188 | aboutBuilder.setMessage("Project is open source and released under Apache License 2.0.\n\nMake sure you read all Legal content.\n\nRaf, 2018. All Rights Reserved."); 189 | aboutBuilder.setNegativeButton("Close", new DialogInterface.OnClickListener() 190 | { 191 | public void onClick(DialogInterface dialog, int id) 192 | { 193 | dialog.dismiss(); 194 | } 195 | }); 196 | AlertDialog aboutDialog = aboutBuilder.create(); 197 | aboutDialog.show(); 198 | return true; 199 | case R.id.menuLegal: 200 | AlertDialog.Builder legalBuilder = new AlertDialog.Builder(this); 201 | legalBuilder.setTitle("Legal"); 202 | legalBuilder.setItems(new CharSequence[]{"License", "Privacy Policy", "Terms and Conditions", "Third Party Notices"}, new DialogInterface.OnClickListener() 203 | { 204 | @Override 205 | public void onClick(DialogInterface dialogInterface, int position) 206 | { 207 | switch(position) 208 | { 209 | case 0: 210 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/h01d/ChatApp/blob/master/LICENSE"))); 211 | break; 212 | case 1: 213 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/h01d/ChatApp/blob/master/PRIVACY_POLICY.md"))); 214 | break; 215 | case 2: 216 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/h01d/ChatApp/blob/master/TERMS_AND_CONDITIONS.md"))); 217 | break; 218 | case 3: 219 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/h01d/ChatApp/blob/master/THIRD_PARTY_NOTICES.md"))); 220 | break; 221 | } 222 | } 223 | }); 224 | 225 | AlertDialog legalDialog = legalBuilder.create(); 226 | legalDialog.show(); 227 | return true; 228 | case R.id.menuProfile: 229 | Intent profileIntent = new Intent(MainActivity.this, ProfileActivity.class); 230 | profileIntent.putExtra("userid", FirebaseAuth.getInstance().getCurrentUser().getUid()); 231 | startActivity(profileIntent); 232 | return true; 233 | case R.id.menuSearch: 234 | Intent usersIntent = new Intent(MainActivity.this, UsersActivity.class); 235 | startActivity(usersIntent); 236 | return true; 237 | default: 238 | return super.onOptionsItemSelected(item); 239 | } 240 | } 241 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/activities/FullScreenActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.activities; 2 | 3 | /** 4 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 5 | * Licensed under Apache License 2.0 6 | * 7 | * @author Raf (https://github.com/h01d) 8 | * @version 1.1 9 | * @since 27/02/2018 10 | */ 11 | 12 | import android.support.v7.app.AppCompatActivity; 13 | import android.os.Bundle; 14 | import android.view.View; 15 | import android.widget.ImageView; 16 | import android.widget.TextView; 17 | 18 | import com.github.h01d.chatapp.R; 19 | import com.squareup.picasso.Callback; 20 | import com.squareup.picasso.NetworkPolicy; 21 | import com.squareup.picasso.Picasso; 22 | 23 | public class FullScreenActivity extends AppCompatActivity 24 | { 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) 27 | { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_full_screen); 30 | 31 | final String url = getIntent().getStringExtra("imageUrl"); 32 | 33 | final ImageView image= findViewById(R.id.a_fullscreen_image); 34 | final TextView message = findViewById(R.id.a_fullscreen_message); 35 | 36 | message.setText("Loading Picture..."); 37 | message.setVisibility(View.VISIBLE); 38 | 39 | Picasso.with(getApplicationContext()) 40 | .load(url) 41 | .networkPolicy(NetworkPolicy.OFFLINE) 42 | .into(image, new Callback() 43 | { 44 | @Override 45 | public void onSuccess() 46 | { 47 | message.setVisibility(View.GONE); 48 | } 49 | 50 | @Override 51 | public void onError() 52 | { 53 | Picasso.with(getApplicationContext()) 54 | .load(url) 55 | .into(image, new Callback() 56 | { 57 | @Override 58 | public void onSuccess() 59 | { 60 | message.setVisibility(View.GONE); 61 | } 62 | 63 | @Override 64 | public void onError() 65 | { 66 | message.setVisibility(View.VISIBLE); 67 | message.setText("Error: Could not load picture."); 68 | } 69 | }); 70 | } 71 | }); 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/activities/UsersActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.activities; 2 | 3 | import android.content.Intent; 4 | import android.support.v4.app.NavUtils; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.support.v7.widget.DividerItemDecoration; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.LayoutInflater; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | import com.firebase.ui.database.FirebaseRecyclerAdapter; 16 | import com.firebase.ui.database.FirebaseRecyclerOptions; 17 | import com.github.h01d.chatapp.R; 18 | import com.github.h01d.chatapp.holders.UserHolder; 19 | import com.github.h01d.chatapp.models.User; 20 | import com.google.firebase.auth.FirebaseAuth; 21 | import com.google.firebase.database.DatabaseReference; 22 | import com.google.firebase.database.FirebaseDatabase; 23 | import com.google.firebase.database.ServerValue; 24 | 25 | /** 26 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 27 | * Licensed under Apache License 2.0 28 | * 29 | * @author Raf (https://github.com/h01d) 30 | * @version 1.1 31 | * @since 27/02/2018 32 | */ 33 | 34 | public class UsersActivity extends AppCompatActivity 35 | { 36 | private final String TAG = "CA/UsersActivity"; 37 | 38 | private FirebaseRecyclerAdapter adapter; 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) 42 | { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_users); 45 | 46 | // RecyclerView related 47 | 48 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext()); 49 | 50 | RecyclerView recyclerView = findViewById(R.id.users_recycler); 51 | recyclerView.setHasFixedSize(true); 52 | recyclerView.setLayoutManager(linearLayoutManager); 53 | 54 | DividerItemDecoration mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), linearLayoutManager.getOrientation()); 55 | recyclerView.addItemDecoration(mDividerItemDecoration); 56 | 57 | // Initializing Users database 58 | 59 | DatabaseReference usersDatabase = FirebaseDatabase.getInstance().getReference().child("Users"); 60 | usersDatabase.keepSynced(true); // For offline use 61 | 62 | // Initializing adapter 63 | 64 | FirebaseRecyclerOptions options = new FirebaseRecyclerOptions.Builder().setQuery(usersDatabase.orderByChild("name"), User.class).build(); 65 | 66 | adapter = new FirebaseRecyclerAdapter(options) 67 | { 68 | @Override 69 | protected void onBindViewHolder(final UserHolder holder, int position, User model) 70 | { 71 | final String userid = getRef(position).getKey(); 72 | 73 | holder.setHolder(userid); 74 | holder.getView().setOnClickListener(new View.OnClickListener() 75 | { 76 | @Override 77 | public void onClick(View view) 78 | { 79 | Intent userProfileIntent = new Intent(UsersActivity.this, ProfileActivity.class); 80 | userProfileIntent.putExtra("userid", userid); 81 | startActivity(userProfileIntent); 82 | } 83 | }); 84 | } 85 | 86 | @Override 87 | public UserHolder onCreateViewHolder(ViewGroup parent, int viewType) 88 | { 89 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user, parent, false); 90 | 91 | return new UserHolder(UsersActivity.this, view, getApplicationContext()); 92 | } 93 | }; 94 | 95 | recyclerView.setAdapter(adapter); 96 | } 97 | 98 | @Override 99 | protected void onStart() 100 | { 101 | super.onStart(); 102 | 103 | adapter.startListening(); 104 | } 105 | 106 | @Override 107 | protected void onResume() 108 | { 109 | super.onResume(); 110 | 111 | FirebaseDatabase.getInstance().getReference().child("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("online").setValue("true"); 112 | } 113 | 114 | @Override 115 | protected void onPause() 116 | { 117 | super.onPause(); 118 | 119 | FirebaseDatabase.getInstance().getReference().child("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("online").setValue(ServerValue.TIMESTAMP); 120 | } 121 | 122 | @Override 123 | protected void onStop() 124 | { 125 | super.onStop(); 126 | 127 | adapter.stopListening(); 128 | } 129 | 130 | @Override 131 | public void onBackPressed() 132 | { 133 | NavUtils.navigateUpFromSameTask(this); 134 | } 135 | 136 | @Override 137 | public boolean onOptionsItemSelected(MenuItem item) 138 | { 139 | switch(item.getItemId()) 140 | { 141 | case android.R.id.home: 142 | NavUtils.navigateUpFromSameTask(this); 143 | break; 144 | } 145 | return super.onOptionsItemSelected(item); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/activities/WelcomeActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.activities; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Handler; 7 | import android.support.annotation.NonNull; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.os.Bundle; 10 | import android.util.Log; 11 | import android.view.KeyEvent; 12 | import android.view.View; 13 | import android.view.inputmethod.InputMethodManager; 14 | import android.widget.EditText; 15 | import android.widget.TextView; 16 | import android.widget.Toast; 17 | 18 | import com.dd.processbutton.iml.ActionProcessButton; 19 | import com.github.h01d.chatapp.MainActivity; 20 | import com.github.h01d.chatapp.R; 21 | import com.google.android.gms.tasks.OnCompleteListener; 22 | import com.google.android.gms.tasks.Task; 23 | import com.google.firebase.auth.AuthResult; 24 | import com.google.firebase.auth.FirebaseAuth; 25 | import com.google.firebase.auth.FirebaseUser; 26 | import com.google.firebase.database.FirebaseDatabase; 27 | import com.google.firebase.database.ServerValue; 28 | import com.google.firebase.iid.FirebaseInstanceId; 29 | import com.sothree.slidinguppanel.SlidingUpPanelLayout; 30 | 31 | import java.util.HashMap; 32 | import java.util.Map; 33 | 34 | import top.wefor.circularanim.CircularAnim; 35 | 36 | /** 37 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 38 | * Licensed under Apache License 2.0 39 | * 40 | * @author Raf (https://github.com/h01d) 41 | * @version 1.1 42 | * @since 27/02/2018 43 | */ 44 | 45 | public class WelcomeActivity extends AppCompatActivity 46 | { 47 | private final String TAG = "CA/WelcomeActivity"; 48 | 49 | @Override 50 | protected void onCreate(Bundle savedInstanceState) 51 | { 52 | super.onCreate(savedInstanceState); 53 | setContentView(R.layout.activity_welcome); 54 | 55 | // activity_welcome views 56 | 57 | final EditText loginEmail = findViewById(R.id.login_email_text); 58 | final EditText loginPassword = findViewById(R.id.login_password_text); 59 | 60 | final EditText registerName = findViewById(R.id.register_name_text); 61 | final EditText registerEmail = findViewById(R.id.register_email_text); 62 | final EditText registerPassword = findViewById(R.id.register_password_text); 63 | 64 | // Setting up login button work 65 | 66 | final ActionProcessButton loginButton = findViewById(R.id.login_button); 67 | loginButton.setProgress(0); 68 | loginButton.setMode(ActionProcessButton.Mode.ENDLESS); 69 | loginButton.setOnClickListener(new View.OnClickListener() 70 | { 71 | @Override 72 | public void onClick(View view) 73 | { 74 | loginButton.setClickable(false); 75 | 76 | loginEmail.clearFocus(); 77 | loginPassword.clearFocus(); 78 | 79 | // Hiding the soft keyboard 80 | 81 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 82 | imm.hideSoftInputFromWindow(loginPassword.getWindowToken(), 0); 83 | 84 | if(loginEmail.getText().length() == 0 || loginPassword.getText().length() == 0) 85 | { 86 | Toast.makeText(getApplicationContext(), "Fields cannot be empty.", Toast.LENGTH_SHORT).show(); 87 | 88 | loginButton.setProgress(-1); 89 | loginButton.setClickable(true); 90 | } 91 | else 92 | { 93 | loginButton.setProgress(1); 94 | 95 | // Loggin user with data he gave us 96 | 97 | FirebaseAuth.getInstance().signInWithEmailAndPassword(loginEmail.getText().toString(), loginPassword.getText().toString()).addOnCompleteListener(new OnCompleteListener() 98 | { 99 | @Override 100 | public void onComplete(@NonNull Task task) 101 | { 102 | if(task.isSuccessful()) 103 | { 104 | String token = FirebaseInstanceId.getInstance().getToken(); 105 | String userid = FirebaseAuth.getInstance().getCurrentUser().getUid(); 106 | 107 | // Updating user device token 108 | 109 | FirebaseDatabase.getInstance().getReference().child("Users").child(userid).child("token").setValue(token).addOnCompleteListener(new OnCompleteListener() 110 | { 111 | @Override 112 | public void onComplete(@NonNull Task task) 113 | { 114 | if(task.isSuccessful()) 115 | { 116 | loginButton.setProgress(100); 117 | 118 | if(FirebaseAuth.getInstance().getCurrentUser().isEmailVerified()) 119 | { 120 | // Show animation and start activity 121 | 122 | new Handler().postDelayed(new Runnable() 123 | { 124 | @Override 125 | public void run() 126 | { 127 | CircularAnim.fullActivity(WelcomeActivity.this, loginButton) 128 | .colorOrImageRes(R.color.colorGreen) 129 | .go(new CircularAnim.OnAnimationEndListener() 130 | { 131 | @Override 132 | public void onAnimationEnd() 133 | { 134 | startActivity(new Intent(WelcomeActivity.this, MainActivity.class)); 135 | finish(); 136 | } 137 | }); 138 | } 139 | }, 1000); 140 | } 141 | else 142 | { 143 | Toast.makeText(getApplicationContext(), "Your email is not verified, we have sent you a new one.", Toast.LENGTH_LONG).show(); 144 | FirebaseAuth.getInstance().signOut(); 145 | 146 | loginButton.setProgress(-1); 147 | loginButton.setClickable(true); 148 | } 149 | } 150 | else 151 | { 152 | Log.d(TAG, "uploadToken failed: " + task.getException().getMessage()); 153 | } 154 | } 155 | }); 156 | } 157 | else 158 | { 159 | Toast.makeText(getApplicationContext(), task.getException().getMessage(), Toast.LENGTH_LONG).show(); 160 | 161 | Log.d(TAG, "signIn failed: " + task.getException().getMessage()); 162 | 163 | loginButton.setProgress(-1); 164 | loginButton.setClickable(true); 165 | } 166 | } 167 | }); 168 | } 169 | } 170 | }); 171 | 172 | // Will handle "enter key" login 173 | 174 | loginPassword.setOnKeyListener(new View.OnKeyListener() 175 | { 176 | public boolean onKey(View v, int keyCode, KeyEvent event) 177 | { 178 | if((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) 179 | { 180 | loginButton.performClick(); 181 | return true; 182 | } 183 | return false; 184 | } 185 | }); 186 | 187 | // Setting up register button 188 | 189 | final ActionProcessButton registerButton = findViewById(R.id.register_button); 190 | registerButton.setProgress(0); 191 | registerButton.setMode(ActionProcessButton.Mode.ENDLESS); 192 | registerButton.setOnClickListener(new View.OnClickListener() 193 | { 194 | @Override 195 | public void onClick(View view) 196 | { 197 | registerButton.setClickable(false); 198 | 199 | loginButton.setClickable(false); 200 | 201 | registerName.clearFocus(); 202 | registerEmail.clearFocus(); 203 | registerPassword.clearFocus(); 204 | 205 | // Hiding soft keyboard 206 | 207 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 208 | imm.hideSoftInputFromWindow(registerButton.getWindowToken(), 0); 209 | 210 | if(registerEmail.getText().toString().length() == 0 || registerPassword.getText().toString().length() == 0) 211 | { 212 | Toast.makeText(getApplicationContext(), "Fields cannot be empty.", Toast.LENGTH_SHORT).show(); 213 | 214 | registerButton.setProgress(-1); 215 | registerButton.setClickable(true); 216 | 217 | loginButton.setClickable(true); 218 | } 219 | else 220 | { 221 | registerButton.setProgress(1); 222 | 223 | // Registering user with data he gave us 224 | 225 | FirebaseAuth.getInstance().createUserWithEmailAndPassword(registerEmail.getText().toString(), registerPassword.getText().toString()).addOnCompleteListener(new OnCompleteListener() 226 | { 227 | @Override 228 | public void onComplete(@NonNull Task task) 229 | { 230 | if(task.isSuccessful()) 231 | { 232 | FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser(); 233 | 234 | if(firebaseUser != null) 235 | { 236 | String userid = firebaseUser.getUid(); 237 | 238 | // "Packing" user data 239 | 240 | Map map = new HashMap<>(); 241 | map.put("token", FirebaseInstanceId.getInstance().getToken()); 242 | map.put("name", registerName.getText().toString()); 243 | map.put("email", registerEmail.getText().toString()); 244 | map.put("status", "Welcome to my Profile!"); 245 | map.put("image", "default"); 246 | map.put("cover", "default"); 247 | map.put("date", ServerValue.TIMESTAMP); 248 | 249 | // Uploading user data 250 | 251 | FirebaseDatabase.getInstance().getReference().child("Users").child(userid).setValue(map).addOnCompleteListener(new OnCompleteListener() 252 | { 253 | @Override 254 | public void onComplete(@NonNull Task task) 255 | { 256 | if(task.isSuccessful()) 257 | { 258 | registerButton.setProgress(100); 259 | 260 | FirebaseAuth.getInstance().getCurrentUser().sendEmailVerification(); 261 | Toast.makeText(getApplicationContext(), "We have sent you a verification email to activate your account.", Toast.LENGTH_LONG).show(); 262 | FirebaseAuth.getInstance().signOut(); 263 | 264 | loginButton.setClickable(true); 265 | } 266 | else 267 | { 268 | Log.d(TAG, "registerData failed: " + task.getException().getMessage()); 269 | } 270 | } 271 | }); 272 | } 273 | } 274 | else 275 | { 276 | Toast.makeText(getApplicationContext(), task.getException().getMessage(), Toast.LENGTH_LONG).show(); 277 | 278 | Log.d(TAG, "createUser failed: " + task.getException().getMessage()); 279 | 280 | registerButton.setProgress(-1); 281 | registerButton.setClickable(true); 282 | 283 | loginButton.setClickable(true); 284 | } 285 | } 286 | }); 287 | } 288 | } 289 | }); 290 | 291 | // Will handle "enter key" register 292 | 293 | registerPassword.setOnKeyListener(new View.OnKeyListener() 294 | { 295 | public boolean onKey(View v, int keyCode, KeyEvent event) 296 | { 297 | if((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) 298 | { 299 | registerButton.performClick(); 300 | return true; 301 | } 302 | return false; 303 | } 304 | }); 305 | 306 | // Will handle Terms and Condition text click 307 | 308 | final TextView registerTerms = findViewById(R.id.register_terms); 309 | registerTerms.setOnClickListener(new View.OnClickListener() 310 | { 311 | @Override 312 | public void onClick(View view) 313 | { 314 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/h01d/ChatApp/blob/master/TERMS_AND_CONDITIONS.md"))); 315 | } 316 | }); 317 | } 318 | 319 | @Override 320 | public void onBackPressed() 321 | { 322 | SlidingUpPanelLayout slidingUpPanelLayout = findViewById(R.id.welcome_sliding); 323 | 324 | if(slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) 325 | { 326 | slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); 327 | } 328 | else 329 | { 330 | super.onBackPressed(); 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/adapters/MessageAdapter.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.adapters; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import com.github.h01d.chatapp.R; 9 | import com.github.h01d.chatapp.models.Message; 10 | import com.github.h01d.chatapp.holders.MessageHolder; 11 | import com.google.firebase.auth.FirebaseAuth; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 17 | * Licensed under Apache License 2.0 18 | * 19 | * @author Raf (https://github.com/h01d) 20 | * @version 1.1 21 | * @since 27/02/2018 22 | */ 23 | 24 | public class MessageAdapter extends RecyclerView.Adapter 25 | { 26 | private List messagesList; 27 | 28 | public MessageAdapter(List messagesList) 29 | { 30 | this.messagesList = messagesList; 31 | } 32 | 33 | @Override 34 | public MessageHolder onCreateViewHolder(ViewGroup parent, int viewType) 35 | { 36 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.message, parent, false); 37 | 38 | return new MessageHolder(view, view.getContext()); 39 | } 40 | 41 | @Override 42 | public void onBindViewHolder(final MessageHolder holder, int position) 43 | { 44 | final String currentUserId = FirebaseAuth.getInstance().getCurrentUser().getUid(); 45 | 46 | Message c = messagesList.get(position); 47 | 48 | if(messagesList.size() - 1 == position) 49 | { 50 | holder.setLastMessage(currentUserId, c.getFrom(), c.getTo()); 51 | } 52 | else 53 | { 54 | holder.hideBottom(); 55 | } 56 | 57 | if(c.getFrom().equals(currentUserId)) 58 | { 59 | holder.setRightMessage(c.getFrom(), c.getMessage(), c.getTimestamp(), c.getType()); 60 | } 61 | else 62 | { 63 | holder.setLeftMessage(c.getFrom(), c.getMessage(), c.getTimestamp(), c.getType()); 64 | } 65 | } 66 | 67 | @Override 68 | public int getItemCount() 69 | { 70 | return messagesList.size(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/fragments/ChatFragment.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.fragments; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.widget.DividerItemDecoration; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.TextView; 14 | 15 | import com.firebase.ui.database.FirebaseRecyclerAdapter; 16 | import com.firebase.ui.database.FirebaseRecyclerOptions; 17 | import com.github.h01d.chatapp.R; 18 | import com.github.h01d.chatapp.activities.ChatActivity; 19 | import com.github.h01d.chatapp.holders.ChatHolder; 20 | import com.github.h01d.chatapp.models.Chat; 21 | import com.google.firebase.auth.FirebaseAuth; 22 | import com.google.firebase.database.DatabaseReference; 23 | import com.google.firebase.database.FirebaseDatabase; 24 | 25 | /** 26 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 27 | * Licensed under Apache License 2.0 28 | * 29 | * @author Raf (https://github.com/h01d) 30 | * @version 1.1 31 | * @since 27/02/2018 32 | */ 33 | 34 | public class ChatFragment extends Fragment 35 | { 36 | private FirebaseRecyclerAdapter adapter; 37 | 38 | public ChatFragment() 39 | { 40 | 41 | } 42 | 43 | @Override 44 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 45 | { 46 | final View view = inflater.inflate(R.layout.fragment_chat, container, false); 47 | 48 | String currentUserId = FirebaseAuth.getInstance().getCurrentUser().getUid(); 49 | 50 | // Initialize Chat Database 51 | 52 | DatabaseReference chatDatabase = FirebaseDatabase.getInstance().getReference().child("Chat").child(currentUserId); 53 | chatDatabase.keepSynced(true); // For offline use 54 | 55 | // RecyclerView related 56 | 57 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); 58 | linearLayoutManager.setReverseLayout(true); 59 | linearLayoutManager.setStackFromEnd(true); 60 | 61 | RecyclerView recyclerView = view.findViewById(R.id.chat_recycler); 62 | recyclerView.setHasFixedSize(true); 63 | recyclerView.setLayoutManager(linearLayoutManager); 64 | 65 | DividerItemDecoration mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), linearLayoutManager.getOrientation()); 66 | recyclerView.addItemDecoration(mDividerItemDecoration); 67 | 68 | // Initializing adapter 69 | 70 | FirebaseRecyclerOptions options = new FirebaseRecyclerOptions.Builder().setQuery(chatDatabase.orderByChild("timestamp"), Chat.class).build(); 71 | 72 | adapter = new FirebaseRecyclerAdapter(options) 73 | { 74 | @Override 75 | public ChatHolder onCreateViewHolder(ViewGroup parent, int viewType) 76 | { 77 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user, parent, false); 78 | 79 | return new ChatHolder(getActivity(), view, getContext()); 80 | } 81 | 82 | @Override 83 | protected void onBindViewHolder(final ChatHolder holder, int position, final Chat model) 84 | { 85 | final String userid = getRef(position).getKey(); 86 | 87 | holder.setHolder(userid, model.getMessage(), model.getTimestamp(), model.getSeen()); 88 | holder.getView().setOnClickListener(new View.OnClickListener() 89 | { 90 | @Override 91 | public void onClick(View view) 92 | { 93 | Intent chatIntent = new Intent(getContext(), ChatActivity.class); 94 | chatIntent.putExtra("userid", userid); 95 | startActivity(chatIntent); 96 | } 97 | }); 98 | } 99 | 100 | @Override 101 | public void onDataChanged() 102 | { 103 | super.onDataChanged(); 104 | 105 | TextView text = view.findViewById(R.id.f_chat_text); 106 | 107 | if(adapter.getItemCount() == 0) 108 | { 109 | text.setVisibility(View.VISIBLE); 110 | } 111 | else 112 | { 113 | text.setVisibility(View.GONE); 114 | } 115 | } 116 | }; 117 | 118 | recyclerView.setAdapter(adapter); 119 | return view; 120 | } 121 | 122 | @Override 123 | public void onStart() 124 | { 125 | super.onStart(); 126 | 127 | adapter.startListening(); 128 | adapter.notifyDataSetChanged(); 129 | } 130 | 131 | @Override 132 | public void onStop() 133 | { 134 | super.onStop(); 135 | 136 | adapter.stopListening(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/fragments/FriendsFragment.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.fragments; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.widget.DividerItemDecoration; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.LayoutInflater; 11 | import android.view.Menu; 12 | import android.view.MenuItem; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.widget.PopupMenu; 16 | import android.widget.TextView; 17 | 18 | import com.firebase.ui.database.FirebaseRecyclerAdapter; 19 | import com.firebase.ui.database.FirebaseRecyclerOptions; 20 | import com.github.h01d.chatapp.R; 21 | import com.github.h01d.chatapp.activities.ChatActivity; 22 | import com.github.h01d.chatapp.activities.ProfileActivity; 23 | import com.github.h01d.chatapp.holders.FriendHolder; 24 | import com.github.h01d.chatapp.models.Friend; 25 | import com.google.firebase.auth.FirebaseAuth; 26 | import com.google.firebase.database.DatabaseReference; 27 | import com.google.firebase.database.FirebaseDatabase; 28 | 29 | /** 30 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 31 | * Licensed under Apache License 2.0 32 | * 33 | * @author Raf (https://github.com/h01d) 34 | * @version 1.1 35 | * @since 27/02/2018 36 | */ 37 | 38 | public class FriendsFragment extends Fragment 39 | { 40 | private FirebaseRecyclerAdapter adapter; 41 | 42 | public FriendsFragment() 43 | { 44 | 45 | } 46 | 47 | @Override 48 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 49 | { 50 | final View view = inflater.inflate(R.layout.fragment_friends, container, false); 51 | 52 | String currentUserId = FirebaseAuth.getInstance().getCurrentUser().getUid(); 53 | 54 | // Initializing Friends database 55 | 56 | DatabaseReference friendsDatabase = FirebaseDatabase.getInstance().getReference().child("Friends").child(currentUserId); 57 | friendsDatabase.keepSynced(true); // For offline use 58 | 59 | // RecyclerView related 60 | 61 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); 62 | linearLayoutManager.setReverseLayout(true); 63 | linearLayoutManager.setStackFromEnd(true); 64 | 65 | final RecyclerView recyclerView = view.findViewById(R.id.friends_recycler); 66 | recyclerView.setHasFixedSize(true); 67 | recyclerView.setLayoutManager(linearLayoutManager); 68 | 69 | DividerItemDecoration mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), linearLayoutManager.getOrientation()); 70 | recyclerView.addItemDecoration(mDividerItemDecoration); 71 | 72 | // Initializing adapter 73 | 74 | FirebaseRecyclerOptions options = new FirebaseRecyclerOptions.Builder().setQuery(friendsDatabase.orderByChild("date"), Friend.class).build(); 75 | 76 | adapter = new FirebaseRecyclerAdapter(options) 77 | { 78 | @Override 79 | protected void onBindViewHolder(final FriendHolder holder, int position, final Friend model) 80 | { 81 | final String userid = getRef(position).getKey(); 82 | 83 | holder.setHolder(userid, model.getDate()); 84 | holder.getView().setOnClickListener(new View.OnClickListener() 85 | { 86 | @Override 87 | public void onClick(View view) 88 | { 89 | PopupMenu popup = new PopupMenu(getContext(), view); 90 | 91 | popup.getMenu().add(Menu.NONE, 1, 1, "View Profile"); 92 | popup.getMenu().add(Menu.NONE, 2, 2, "Send Message"); 93 | 94 | popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() 95 | { 96 | @Override 97 | public boolean onMenuItemClick(MenuItem menuItem) 98 | { 99 | switch(menuItem.getItemId()) 100 | { 101 | case 1: 102 | Intent userProfileIntent = new Intent(getContext(), ProfileActivity.class); 103 | userProfileIntent.putExtra("userid", userid); 104 | startActivity(userProfileIntent); 105 | return true; 106 | case 2: 107 | Intent sendMessageIntent = new Intent(getContext(), ChatActivity.class); 108 | sendMessageIntent.putExtra("userid", userid); 109 | startActivity(sendMessageIntent); 110 | return true; 111 | default: 112 | return false; 113 | } 114 | } 115 | }); 116 | popup.show(); 117 | } 118 | }); 119 | } 120 | 121 | @Override 122 | public FriendHolder onCreateViewHolder(ViewGroup parent, int viewType) 123 | { 124 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user, parent, false); 125 | 126 | return new FriendHolder(getActivity(), view, getContext()); 127 | } 128 | 129 | @Override 130 | public void onDataChanged() 131 | { 132 | super.onDataChanged(); 133 | 134 | TextView text = view.findViewById(R.id.f_friends_text); 135 | 136 | if(adapter.getItemCount() == 0) 137 | { 138 | text.setVisibility(View.VISIBLE); 139 | } 140 | else 141 | { 142 | text.setVisibility(View.GONE); 143 | } 144 | 145 | recyclerView.scrollToPosition(adapter.getItemCount() - 1); 146 | } 147 | }; 148 | 149 | recyclerView.setAdapter(adapter); 150 | return view; 151 | } 152 | 153 | public void onStart() 154 | { 155 | super.onStart(); 156 | 157 | adapter.startListening(); 158 | adapter.notifyDataSetChanged(); 159 | } 160 | 161 | @Override 162 | public void onStop() 163 | { 164 | super.onStop(); 165 | 166 | adapter.stopListening(); 167 | } 168 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/fragments/RequestsFragment.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.fragments; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.widget.DividerItemDecoration; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.util.Log; 11 | import android.view.LayoutInflater; 12 | import android.view.Menu; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.PopupMenu; 17 | import android.widget.TextView; 18 | import android.widget.Toast; 19 | 20 | import com.firebase.ui.database.FirebaseRecyclerAdapter; 21 | import com.firebase.ui.database.FirebaseRecyclerOptions; 22 | import com.github.h01d.chatapp.R; 23 | import com.github.h01d.chatapp.activities.ProfileActivity; 24 | import com.github.h01d.chatapp.holders.RequestHolder; 25 | import com.github.h01d.chatapp.models.Request; 26 | import com.google.firebase.auth.FirebaseAuth; 27 | import com.google.firebase.database.DatabaseError; 28 | import com.google.firebase.database.DatabaseReference; 29 | import com.google.firebase.database.FirebaseDatabase; 30 | import com.google.firebase.database.ServerValue; 31 | 32 | import java.util.HashMap; 33 | import java.util.Map; 34 | 35 | /** 36 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 37 | * Licensed under Apache License 2.0 38 | * 39 | * @author Raf (https://github.com/h01d) 40 | * @version 1.1 41 | * @since 27/02/2018 42 | */ 43 | 44 | public class RequestsFragment extends Fragment 45 | { 46 | private final String TAG = "CA/RequestsFragment"; 47 | 48 | private FirebaseRecyclerAdapter adapter; 49 | 50 | public RequestsFragment() 51 | { 52 | 53 | } 54 | 55 | @Override 56 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 57 | { 58 | final View view = inflater.inflate(R.layout.fragment_request, container, false); 59 | 60 | final String currentUserId = FirebaseAuth.getInstance().getCurrentUser().getUid(); 61 | 62 | // Initializing Request database 63 | 64 | DatabaseReference requestsDatabase = FirebaseDatabase.getInstance().getReference().child("Requests").child(currentUserId); 65 | requestsDatabase.keepSynced(true); // For offline use 66 | 67 | // RecyclerView related 68 | 69 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); 70 | 71 | RecyclerView recyclerView = view.findViewById(R.id.f_request_recycler); 72 | recyclerView.setHasFixedSize(true); 73 | recyclerView.setLayoutManager(linearLayoutManager); 74 | 75 | DividerItemDecoration mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), linearLayoutManager.getOrientation()); 76 | recyclerView.addItemDecoration(mDividerItemDecoration); 77 | 78 | // Initializing adapter 79 | 80 | FirebaseRecyclerOptions options = new FirebaseRecyclerOptions.Builder().setQuery(requestsDatabase.orderByChild("type"), Request.class).build(); 81 | 82 | adapter = new FirebaseRecyclerAdapter(options) 83 | { 84 | @Override 85 | protected void onBindViewHolder(final RequestHolder holder, int position, final Request model) 86 | { 87 | if(model.getType().equals("sent")) 88 | { 89 | holder.getView().setVisibility(View.GONE); 90 | } 91 | else 92 | { 93 | final String userid = getRef(position).getKey(); 94 | 95 | holder.setHolder(userid); 96 | holder.getView().setOnClickListener(new View.OnClickListener() 97 | { 98 | @Override 99 | public void onClick(View view) 100 | { 101 | PopupMenu popup = new PopupMenu(getContext(), view); 102 | 103 | popup.getMenu().add(Menu.NONE, 1, 1, "View Profile"); 104 | popup.getMenu().add(Menu.NONE, 2, 2, "Accept Request"); 105 | 106 | popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() 107 | { 108 | @Override 109 | public boolean onMenuItemClick(MenuItem menuItem) 110 | { 111 | switch(menuItem.getItemId()) 112 | { 113 | case 1: 114 | Intent userProfileIntent = new Intent(getContext(), ProfileActivity.class); 115 | userProfileIntent.putExtra("userid", userid); 116 | startActivity(userProfileIntent); 117 | return true; 118 | case 2: 119 | // Pushing notification to get keyId 120 | 121 | DatabaseReference acceptNotificationRef = FirebaseDatabase.getInstance().getReference().child("Notifications").child(userid).push(); 122 | String acceptNotificationId = acceptNotificationRef.getKey(); 123 | 124 | // "Packing" request 125 | 126 | HashMap acceptNotificationData = new HashMap<>(); 127 | acceptNotificationData.put("from", currentUserId); 128 | acceptNotificationData.put("type", "accept"); 129 | 130 | // "Packing" data 131 | 132 | Map map = new HashMap<>(); 133 | map.put("Friends/" + userid + "/" + currentUserId + "/date", ServerValue.TIMESTAMP); 134 | map.put("Friends/" + currentUserId + "/" + userid + "/date", ServerValue.TIMESTAMP); 135 | 136 | map.put("Requests/" + userid + "/" + currentUserId, null); 137 | map.put("Requests/" + currentUserId + "/" + userid, null); 138 | 139 | map.put("Notifications/" + userid + "/" + acceptNotificationId, acceptNotificationData); 140 | 141 | // Updating data 142 | 143 | FirebaseDatabase.getInstance().getReference().updateChildren(map, new DatabaseReference.CompletionListener() 144 | { 145 | @Override 146 | public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) 147 | { 148 | if(databaseError == null) 149 | { 150 | Toast.makeText(getContext(), "You are now friends!", Toast.LENGTH_SHORT).show(); 151 | } 152 | else 153 | { 154 | Log.d(TAG, "acceptRequest failed: " + databaseError.getMessage()); 155 | } 156 | } 157 | }); 158 | return true; 159 | default: 160 | return false; 161 | } 162 | } 163 | }); 164 | popup.show(); 165 | } 166 | }); 167 | } 168 | } 169 | 170 | @Override 171 | public RequestHolder onCreateViewHolder(ViewGroup parent, int viewType) 172 | { 173 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user, parent, false); 174 | 175 | return new RequestHolder(getActivity(), view, getContext()); 176 | } 177 | 178 | @Override 179 | public void onDataChanged() 180 | { 181 | super.onDataChanged(); 182 | 183 | int counter = 0; 184 | 185 | for(int i = 0; i < adapter.getItemCount(); i++) 186 | { 187 | Request tmp = (Request) adapter.getItem(i); 188 | 189 | if(tmp != null && tmp.getType().equals("received")) 190 | { 191 | counter++; 192 | } 193 | } 194 | 195 | TextView text = view.findViewById(R.id.f_request_text); 196 | 197 | if(counter == 0) 198 | { 199 | text.setVisibility(View.VISIBLE); 200 | } 201 | else 202 | { 203 | text.setVisibility(View.GONE); 204 | } 205 | } 206 | }; 207 | 208 | recyclerView.setAdapter(adapter); 209 | return view; 210 | } 211 | 212 | public void onStart() 213 | { 214 | super.onStart(); 215 | 216 | adapter.startListening(); 217 | adapter.notifyDataSetChanged(); 218 | } 219 | 220 | @Override 221 | public void onStop() 222 | { 223 | super.onStop(); 224 | 225 | adapter.stopListening(); 226 | } 227 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/holders/ChatHolder.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.holders; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Typeface; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.util.Log; 8 | import android.util.TypedValue; 9 | import android.view.View; 10 | import android.widget.ImageView; 11 | import android.widget.TextView; 12 | 13 | import com.github.h01d.chatapp.R; 14 | import com.google.firebase.database.DataSnapshot; 15 | import com.google.firebase.database.DatabaseError; 16 | import com.google.firebase.database.DatabaseReference; 17 | import com.google.firebase.database.FirebaseDatabase; 18 | import com.google.firebase.database.ValueEventListener; 19 | import com.squareup.picasso.Callback; 20 | import com.squareup.picasso.NetworkPolicy; 21 | import com.squareup.picasso.Picasso; 22 | 23 | import java.text.SimpleDateFormat; 24 | import java.util.Locale; 25 | import java.util.Timer; 26 | import java.util.TimerTask; 27 | 28 | import de.hdodenhof.circleimageview.CircleImageView; 29 | 30 | /** 31 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 32 | * Licensed under Apache License 2.0 33 | * 34 | * @author Raf (https://github.com/h01d) 35 | * @version 1.1 36 | * @since 27/02/2018 37 | */ 38 | 39 | public class ChatHolder extends RecyclerView.ViewHolder 40 | { 41 | private final String TAG = "CA/ChatHolder"; 42 | 43 | private Activity activity; 44 | private View view; 45 | private Context context; 46 | 47 | // Will handle user data 48 | 49 | private DatabaseReference userDatabase; 50 | private ValueEventListener userListener; 51 | 52 | public ChatHolder(Activity activity, View view, Context context) 53 | { 54 | super(view); 55 | 56 | this.activity = activity; 57 | this.view = view; 58 | this.context = context; 59 | } 60 | 61 | public View getView() 62 | { 63 | return view; 64 | } 65 | 66 | 67 | public void setHolder(String userid, String message, long timestamp, long seen) 68 | { 69 | final TextView userName = view.findViewById(R.id.user_name); 70 | final TextView userStatus = view.findViewById(R.id.user_status); 71 | final TextView userTime = view.findViewById(R.id.user_timestamp); 72 | final CircleImageView userImage = view.findViewById(R.id.user_image); 73 | final ImageView userOnline = view.findViewById(R.id.user_online); 74 | 75 | userStatus.setText(message); 76 | 77 | userTime.setVisibility(View.VISIBLE); 78 | userTime.setText(new SimpleDateFormat("MMM d, HH:mm", Locale.getDefault()).format(timestamp)); 79 | 80 | if(seen == 0L) 81 | { 82 | userStatus.setTypeface(null, Typeface.BOLD); 83 | userTime.setTypeface(null, Typeface.BOLD); 84 | } 85 | else 86 | { 87 | userStatus.setTypeface(null, Typeface.NORMAL); 88 | userTime.setTypeface(null, Typeface.NORMAL); 89 | } 90 | 91 | if(userDatabase != null && userListener != null) 92 | { 93 | userDatabase.removeEventListener(userListener); 94 | } 95 | 96 | // Initialize/Update user data 97 | 98 | userDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(userid); 99 | userListener = new ValueEventListener() 100 | { 101 | Timer timer; // Will be used to avoid flickering online status when changing activity 102 | 103 | @Override 104 | public void onDataChange(DataSnapshot dataSnapshot) 105 | { 106 | try 107 | { 108 | final String name = dataSnapshot.child("name").getValue().toString(); 109 | final String image = dataSnapshot.child("image").getValue().toString(); 110 | 111 | if(dataSnapshot.hasChild("online")) 112 | { 113 | String online = dataSnapshot.child("online").getValue().toString(); 114 | 115 | if(online.equals("true")) 116 | { 117 | if(timer != null) 118 | { 119 | timer.cancel(); 120 | timer = null; 121 | } 122 | 123 | userOnline.setVisibility(View.VISIBLE); 124 | } 125 | else 126 | { 127 | if(userName.getText().toString().equals("")) 128 | { 129 | userOnline.setVisibility(View.INVISIBLE); 130 | } 131 | else 132 | { 133 | timer = new Timer(); 134 | timer.schedule(new TimerTask() 135 | { 136 | @Override 137 | public void run() 138 | { 139 | activity.runOnUiThread(new Runnable() 140 | { 141 | @Override 142 | public void run() 143 | { 144 | userOnline.setVisibility(View.INVISIBLE); 145 | } 146 | }); 147 | } 148 | }, 2000); 149 | } 150 | } 151 | } 152 | 153 | userName.setText(name); 154 | 155 | if(!image.equals("default")) 156 | { 157 | Picasso.with(context) 158 | .load(image) 159 | .networkPolicy(NetworkPolicy.OFFLINE) 160 | .resize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics())) 161 | .centerCrop() 162 | .placeholder(R.drawable.user) 163 | .into(userImage, new Callback() 164 | { 165 | @Override 166 | public void onSuccess() 167 | { 168 | 169 | } 170 | 171 | @Override 172 | public void onError() 173 | { 174 | Picasso.with(context) 175 | .load(image) 176 | .resize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics())) 177 | .centerCrop() 178 | .placeholder(R.drawable.user) 179 | .error(R.drawable.user) 180 | .into(userImage); 181 | } 182 | }); 183 | } 184 | else 185 | { 186 | userImage.setImageResource(R.drawable.user); 187 | } 188 | } 189 | catch(Exception e) 190 | { 191 | Log.d(TAG, "userListener exception: " + e.getMessage()); 192 | e.printStackTrace(); 193 | } 194 | } 195 | 196 | @Override 197 | public void onCancelled(DatabaseError databaseError) 198 | { 199 | Log.d(TAG, "userListener failed: " + databaseError.getMessage()); 200 | } 201 | }; 202 | userDatabase.addValueEventListener(userListener); 203 | } 204 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/holders/FriendHolder.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.holders; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.util.Log; 7 | import android.util.TypedValue; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.github.h01d.chatapp.R; 13 | import com.google.firebase.database.DataSnapshot; 14 | import com.google.firebase.database.DatabaseError; 15 | import com.google.firebase.database.DatabaseReference; 16 | import com.google.firebase.database.FirebaseDatabase; 17 | import com.google.firebase.database.ValueEventListener; 18 | import com.squareup.picasso.Callback; 19 | import com.squareup.picasso.NetworkPolicy; 20 | import com.squareup.picasso.Picasso; 21 | 22 | import java.text.SimpleDateFormat; 23 | import java.util.Locale; 24 | import java.util.Timer; 25 | import java.util.TimerTask; 26 | 27 | import de.hdodenhof.circleimageview.CircleImageView; 28 | 29 | /** 30 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 31 | * Licensed under Apache License 2.0 32 | * 33 | * @author Raf (https://github.com/h01d) 34 | * @version 1.1 35 | * @since 27/02/2018 36 | */ 37 | 38 | public class FriendHolder extends RecyclerView.ViewHolder 39 | { 40 | private final String TAG = "CA/FriendHolder"; 41 | 42 | private Activity activity; 43 | private View view; 44 | private Context context; 45 | 46 | // Will handle user data 47 | 48 | private DatabaseReference userDatabase; 49 | private ValueEventListener userListener; 50 | 51 | public FriendHolder(Activity activity, View view, Context context) 52 | { 53 | super(view); 54 | 55 | this.activity = activity; 56 | this.view = view; 57 | this.context = context; 58 | } 59 | 60 | public View getView() 61 | { 62 | return view; 63 | } 64 | 65 | public void setHolder(String userid, long date) 66 | { 67 | final TextView userName = view.findViewById(R.id.user_name); 68 | final TextView userStatus = view.findViewById(R.id.user_status); 69 | final CircleImageView userImage = view.findViewById(R.id.user_image); 70 | final ImageView userOnline = view.findViewById(R.id.user_online); 71 | 72 | userStatus.setText("Friends Since: " + new SimpleDateFormat("MMM d, yyyy", Locale.getDefault()).format(date)); 73 | 74 | if(userDatabase != null & userListener != null) 75 | { 76 | userDatabase.removeEventListener(userListener); 77 | } 78 | 79 | // Initialize/Update user data 80 | 81 | userDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(userid); 82 | userListener = new ValueEventListener() 83 | { 84 | Timer timer; // Will be used to avoid flickering online status when changing activity 85 | 86 | @Override 87 | public void onDataChange(DataSnapshot dataSnapshot) 88 | { 89 | try 90 | { 91 | final String name = dataSnapshot.child("name").getValue().toString(); 92 | final String image = dataSnapshot.child("image").getValue().toString(); 93 | 94 | if(dataSnapshot.hasChild("online")) 95 | { 96 | String online = dataSnapshot.child("online").getValue().toString(); 97 | 98 | if(online.equals("true")) 99 | { 100 | if(timer != null) 101 | { 102 | timer.cancel(); 103 | timer = null; 104 | } 105 | 106 | userOnline.setVisibility(View.VISIBLE); 107 | } 108 | else 109 | { 110 | if(userName.getText().toString().equals("")) 111 | { 112 | userOnline.setVisibility(View.INVISIBLE); 113 | } 114 | else 115 | { 116 | timer = new Timer(); 117 | timer.schedule(new TimerTask() 118 | { 119 | @Override 120 | public void run() 121 | { 122 | activity.runOnUiThread(new Runnable() 123 | { 124 | @Override 125 | public void run() 126 | { 127 | userOnline.setVisibility(View.INVISIBLE); 128 | } 129 | }); 130 | } 131 | }, 2000); 132 | } 133 | } 134 | } 135 | 136 | userName.setText(name); 137 | 138 | if(!image.equals("default")) 139 | { 140 | Picasso.with(context) 141 | .load(image) 142 | .networkPolicy(NetworkPolicy.OFFLINE) 143 | .resize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics())) 144 | .centerCrop() 145 | .placeholder(R.drawable.user) 146 | .into(userImage, new Callback() 147 | { 148 | @Override 149 | public void onSuccess() 150 | { 151 | 152 | } 153 | 154 | @Override 155 | public void onError() 156 | { 157 | Picasso.with(context) 158 | .load(image) 159 | .resize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics())) 160 | .centerCrop() 161 | .placeholder(R.drawable.user) 162 | .error(R.drawable.user) 163 | .into(userImage); 164 | } 165 | }); 166 | } 167 | else 168 | { 169 | userImage.setImageResource(R.drawable.user); 170 | } 171 | } 172 | catch(Exception e) 173 | { 174 | Log.d(TAG, "userListener exception: " + e.getMessage()); 175 | e.printStackTrace(); 176 | } 177 | } 178 | 179 | @Override 180 | public void onCancelled(DatabaseError databaseError) 181 | { 182 | Log.d(TAG, "userListener failed: " + databaseError.getMessage()); 183 | } 184 | }; 185 | userDatabase.addValueEventListener(userListener); 186 | } 187 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/holders/RequestHolder.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.holders; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.util.Log; 7 | import android.util.TypedValue; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.github.h01d.chatapp.R; 13 | import com.google.firebase.database.DataSnapshot; 14 | import com.google.firebase.database.DatabaseError; 15 | import com.google.firebase.database.DatabaseReference; 16 | import com.google.firebase.database.FirebaseDatabase; 17 | import com.google.firebase.database.ValueEventListener; 18 | import com.squareup.picasso.Callback; 19 | import com.squareup.picasso.NetworkPolicy; 20 | import com.squareup.picasso.Picasso; 21 | 22 | import java.util.Timer; 23 | import java.util.TimerTask; 24 | 25 | import de.hdodenhof.circleimageview.CircleImageView; 26 | 27 | /** 28 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 29 | * Licensed under Apache License 2.0 30 | * 31 | * @author Raf (https://github.com/h01d) 32 | * @version 1.1 33 | * @since 27/02/2018 34 | */ 35 | 36 | public class RequestHolder extends RecyclerView.ViewHolder 37 | { 38 | private final String TAG = "CA/RequestHolder"; 39 | 40 | private Activity activity; 41 | private View view; 42 | private Context context; 43 | 44 | // Will handle user data 45 | 46 | private DatabaseReference userDatabase; 47 | private ValueEventListener userListener; 48 | 49 | public RequestHolder(Activity activity, View view, Context context) 50 | { 51 | super(view); 52 | 53 | this.activity = activity; 54 | this.view = view; 55 | this.context = context; 56 | } 57 | 58 | public View getView() 59 | { 60 | return view; 61 | } 62 | 63 | public void setHolder(String userid) 64 | { 65 | final TextView userName = view.findViewById(R.id.user_name); 66 | final TextView userStatus = view.findViewById(R.id.user_status); 67 | final CircleImageView userImage = view.findViewById(R.id.user_image); 68 | final ImageView userOnline = view.findViewById(R.id.user_online); 69 | 70 | userStatus.setText("Wants to be your Friend!"); 71 | 72 | if(userDatabase != null & userListener != null) 73 | { 74 | userDatabase.removeEventListener(userListener); 75 | } 76 | 77 | // Initilize/Update user data 78 | 79 | userDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(userid); 80 | userListener = new ValueEventListener() 81 | { 82 | Timer timer; // Will be used to avoid flickering online status when changing activity 83 | 84 | @Override 85 | public void onDataChange(DataSnapshot dataSnapshot) 86 | { 87 | try 88 | { 89 | final String name = dataSnapshot.child("name").getValue().toString(); 90 | final String image = dataSnapshot.child("image").getValue().toString(); 91 | 92 | if(dataSnapshot.hasChild("online")) 93 | { 94 | String online = dataSnapshot.child("online").getValue().toString(); 95 | 96 | if(online.equals("true")) 97 | { 98 | if(timer != null) 99 | { 100 | timer.cancel(); 101 | timer = null; 102 | } 103 | 104 | userOnline.setVisibility(View.VISIBLE); 105 | } 106 | else 107 | { 108 | if(userName.getText().toString().equals("")) 109 | { 110 | userOnline.setVisibility(View.INVISIBLE); 111 | } 112 | else 113 | { 114 | timer = new Timer(); 115 | timer.schedule(new TimerTask() 116 | { 117 | @Override 118 | public void run() 119 | { 120 | activity.runOnUiThread(new Runnable() 121 | { 122 | @Override 123 | public void run() 124 | { 125 | userOnline.setVisibility(View.INVISIBLE); 126 | } 127 | }); 128 | } 129 | }, 2000); 130 | } 131 | } 132 | } 133 | 134 | userName.setText(name); 135 | 136 | if(!image.equals("default")) 137 | { 138 | Picasso.with(context) 139 | .load(image) 140 | .resize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics())) 141 | .centerCrop() 142 | .networkPolicy(NetworkPolicy.OFFLINE) 143 | .placeholder(R.drawable.user) 144 | .into(userImage, new Callback() 145 | { 146 | @Override 147 | public void onSuccess() 148 | { 149 | 150 | } 151 | 152 | @Override 153 | public void onError() 154 | { 155 | Picasso.with(context) 156 | .load(image) 157 | .resize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics())) 158 | .centerCrop() 159 | .placeholder(R.drawable.user) 160 | .error(R.drawable.user) 161 | .into(userImage); 162 | } 163 | }); 164 | } 165 | else 166 | { 167 | userImage.setImageResource(R.drawable.user); 168 | } 169 | } 170 | catch(Exception e) 171 | { 172 | Log.d(TAG, "userListener exception: " + e.getMessage()); 173 | e.printStackTrace(); 174 | } 175 | } 176 | 177 | @Override 178 | public void onCancelled(DatabaseError databaseError) 179 | { 180 | Log.d(TAG, "userListener failed: " + databaseError.getMessage()); 181 | } 182 | }; 183 | userDatabase.addValueEventListener(userListener); 184 | } 185 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/holders/UserHolder.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.holders; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.util.Log; 7 | import android.util.TypedValue; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.github.h01d.chatapp.R; 13 | import com.google.firebase.database.DataSnapshot; 14 | import com.google.firebase.database.DatabaseError; 15 | import com.google.firebase.database.DatabaseReference; 16 | import com.google.firebase.database.FirebaseDatabase; 17 | import com.google.firebase.database.ValueEventListener; 18 | import com.squareup.picasso.Callback; 19 | import com.squareup.picasso.NetworkPolicy; 20 | import com.squareup.picasso.Picasso; 21 | 22 | import java.util.Timer; 23 | import java.util.TimerTask; 24 | 25 | import de.hdodenhof.circleimageview.CircleImageView; 26 | 27 | /** 28 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 29 | * Licensed under Apache License 2.0 30 | * 31 | * @author Raf (https://github.com/h01d) 32 | * @version 1.1 33 | * @since 27/02/2018 34 | */ 35 | 36 | public class UserHolder extends RecyclerView.ViewHolder 37 | { 38 | private final String TAG = "CA/UserHolder"; 39 | 40 | private Activity activity; 41 | private View view; 42 | private Context context; 43 | 44 | // Will handle user data 45 | 46 | private DatabaseReference userDatabase; 47 | private ValueEventListener userListener; 48 | 49 | public UserHolder(Activity activity, View view, Context context) 50 | { 51 | super(view); 52 | 53 | this.activity = activity; 54 | this.view = view; 55 | this.context = context; 56 | } 57 | 58 | public View getView() 59 | { 60 | return view; 61 | } 62 | 63 | public void setHolder(String userid) 64 | { 65 | final TextView userName = view.findViewById(R.id.user_name); 66 | final TextView userStatus = view.findViewById(R.id.user_status); 67 | final CircleImageView userImage = view.findViewById(R.id.user_image); 68 | final ImageView userOnline = view.findViewById(R.id.user_online); 69 | 70 | if(userDatabase != null & userListener != null) 71 | { 72 | userDatabase.removeEventListener(userListener); 73 | } 74 | 75 | // Initialize/Upadte user data 76 | 77 | userDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(userid); 78 | userListener = new ValueEventListener() 79 | { 80 | Timer timer; // Will be used to avoid flickering online status when changing activity 81 | 82 | @Override 83 | public void onDataChange(DataSnapshot dataSnapshot) 84 | { 85 | try 86 | { 87 | final String name = dataSnapshot.child("name").getValue().toString(); 88 | final String status = dataSnapshot.child("status").getValue().toString(); 89 | final String image = dataSnapshot.child("image").getValue().toString(); 90 | 91 | if(dataSnapshot.hasChild("online")) 92 | { 93 | String online = dataSnapshot.child("online").getValue().toString(); 94 | 95 | if(online.equals("true")) 96 | { 97 | if(timer != null) 98 | { 99 | timer.cancel(); 100 | timer = null; 101 | } 102 | 103 | userOnline.setVisibility(View.VISIBLE); 104 | } 105 | else 106 | { 107 | if(userName.getText().toString().equals("")) 108 | { 109 | userOnline.setVisibility(View.INVISIBLE); 110 | } 111 | else 112 | { 113 | timer = new Timer(); 114 | timer.schedule(new TimerTask() 115 | { 116 | @Override 117 | public void run() 118 | { 119 | activity.runOnUiThread(new Runnable() 120 | { 121 | @Override 122 | public void run() 123 | { 124 | userOnline.setVisibility(View.INVISIBLE); 125 | } 126 | }); 127 | } 128 | }, 2000); 129 | } 130 | } 131 | } 132 | 133 | userName.setText(name); 134 | userStatus.setText(status); 135 | 136 | if(!image.equals("default")) 137 | { 138 | Picasso.with(context) 139 | .load(image) 140 | .resize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics())) 141 | .centerCrop() 142 | .networkPolicy(NetworkPolicy.OFFLINE) 143 | .placeholder(R.drawable.user) 144 | .into(userImage, new Callback() 145 | { 146 | @Override 147 | public void onSuccess() 148 | { 149 | 150 | } 151 | 152 | @Override 153 | public void onError() 154 | { 155 | Picasso.with(context) 156 | .load(image) 157 | .resize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics())) 158 | .centerCrop() 159 | .placeholder(R.drawable.user) 160 | .error(R.drawable.user) 161 | .into(userImage); 162 | } 163 | }); 164 | } 165 | else 166 | { 167 | userImage.setImageResource(R.drawable.user); 168 | } 169 | } 170 | catch(Exception e) 171 | { 172 | Log.d(TAG, "userListener exception: " + e.getMessage()); 173 | e.printStackTrace(); 174 | } 175 | } 176 | 177 | @Override 178 | public void onCancelled(DatabaseError databaseError) 179 | { 180 | Log.d(TAG, "userListener failed: " + databaseError.getMessage()); 181 | } 182 | }; 183 | userDatabase.addValueEventListener(userListener); 184 | } 185 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/models/Chat.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.models; 2 | 3 | /** 4 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 5 | * Licensed under Apache License 2.0 6 | * 7 | * @author Raf (https://github.com/h01d) 8 | * @version 1.1 9 | * @since 27/02/2018 10 | */ 11 | 12 | public class Chat 13 | { 14 | private String message; 15 | private int typing; 16 | private long timestamp, seen; 17 | 18 | public Chat() 19 | { 20 | 21 | } 22 | 23 | public Chat(String message, int typing, long timestamp, long seen) 24 | { 25 | this.message = message; 26 | this.typing = typing; 27 | this.timestamp = timestamp; 28 | this.seen = seen; 29 | } 30 | 31 | public String getMessage() 32 | { 33 | return message; 34 | } 35 | 36 | public void setMessage(String message) 37 | { 38 | this.message = message; 39 | } 40 | 41 | public int getTyping() 42 | { 43 | return typing; 44 | } 45 | 46 | public void setTyping(int typing) 47 | { 48 | this.typing = typing; 49 | } 50 | 51 | public long getTimestamp() 52 | { 53 | return timestamp; 54 | } 55 | 56 | public void setTimestamp(long timestamp) 57 | { 58 | this.timestamp = timestamp; 59 | } 60 | 61 | public long getSeen() 62 | { 63 | return seen; 64 | } 65 | 66 | public void setSeen(long seen) 67 | { 68 | this.seen = seen; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/models/Friend.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.models; 2 | 3 | /** 4 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 5 | * Licensed under Apache License 2.0 6 | * 7 | * @author Raf (https://github.com/h01d) 8 | * @version 1.1 9 | * @since 27/02/2018 10 | */ 11 | 12 | public class Friend 13 | { 14 | private long date; 15 | 16 | public Friend() 17 | { 18 | 19 | } 20 | 21 | public Friend(long date) 22 | { 23 | this.date = date; 24 | } 25 | 26 | public long getDate() 27 | { 28 | return date; 29 | } 30 | 31 | public void setDate(long date) 32 | { 33 | this.date = date; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/models/Message.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.models; 2 | 3 | /** 4 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 5 | * Licensed under Apache License 2.0 6 | * 7 | * @author Raf (https://github.com/h01d) 8 | * @version 1.1 9 | * @since 27/02/2018 10 | */ 11 | 12 | public class Message 13 | { 14 | private String message, type, from, to; 15 | private long timestamp; 16 | 17 | public Message() 18 | { 19 | 20 | } 21 | 22 | public Message(String message, String type, String from, String to, long timestamp) 23 | { 24 | this.message = message; 25 | this.type = type; 26 | this.from = from; 27 | this.to = to; 28 | this.timestamp = timestamp; 29 | } 30 | 31 | public String getMessage() 32 | { 33 | return message; 34 | } 35 | 36 | public void setMessage(String message) 37 | { 38 | this.message = message; 39 | } 40 | 41 | public String getType() 42 | { 43 | return type; 44 | } 45 | 46 | public void setType(String type) 47 | { 48 | this.type = type; 49 | } 50 | 51 | public String getFrom() 52 | { 53 | return from; 54 | } 55 | 56 | public void setFrom(String from) 57 | { 58 | this.from = from; 59 | } 60 | 61 | public String getTo() 62 | { 63 | return to; 64 | } 65 | 66 | public void setTo(String to) 67 | { 68 | this.to = to; 69 | } 70 | 71 | public long getTimestamp() 72 | { 73 | return timestamp; 74 | } 75 | 76 | public void setTimestamp(long timestamp) 77 | { 78 | this.timestamp = timestamp; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/models/Request.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.models; 2 | 3 | /** 4 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 5 | * Licensed under Apache License 2.0 6 | * 7 | * @author Raf (https://github.com/h01d) 8 | * @version 1.1 9 | * @since 27/02/2018 10 | */ 11 | 12 | public class Request 13 | { 14 | private String type; 15 | 16 | public Request() 17 | { 18 | 19 | } 20 | 21 | public Request(String type) 22 | { 23 | this.type = type; 24 | } 25 | 26 | public String getType() 27 | { 28 | return type; 29 | } 30 | 31 | public void setType(String type) 32 | { 33 | this.type = type; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/models/User.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.models; 2 | 3 | /** 4 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 5 | * Licensed under Apache License 2.0 6 | * 7 | * @author Raf (https://github.com/h01d) 8 | * @version 1.1 9 | * @since 27/02/2018 10 | */ 11 | 12 | public class User 13 | { 14 | private String token, name, email, status, image, cover; 15 | private long date; 16 | 17 | public User() 18 | { 19 | 20 | } 21 | 22 | public User(String token, String name, String email, String status, String image, String cover, long date) 23 | { 24 | this.token = token; 25 | this.name = name; 26 | this.email = email; 27 | this.status = status; 28 | this.image = image; 29 | this.cover = cover; 30 | this.date = date; 31 | } 32 | 33 | public String getToken() 34 | { 35 | return token; 36 | } 37 | 38 | public void setToken(String token) 39 | { 40 | this.token = token; 41 | } 42 | 43 | public String getName() 44 | { 45 | return name; 46 | } 47 | 48 | public void setName(String name) 49 | { 50 | this.name = name; 51 | } 52 | 53 | public String getEmail() 54 | { 55 | return email; 56 | } 57 | 58 | public void setEmail(String email) 59 | { 60 | this.email = email; 61 | } 62 | 63 | public String getStatus() 64 | { 65 | return status; 66 | } 67 | 68 | public void setStatus(String status) 69 | { 70 | this.status = status; 71 | } 72 | 73 | public String getImage() 74 | { 75 | return image; 76 | } 77 | 78 | public void setImage(String image) 79 | { 80 | this.image = image; 81 | } 82 | 83 | public String getCover() 84 | { 85 | return cover; 86 | } 87 | 88 | public void setCover(String cover) 89 | { 90 | this.cover = cover; 91 | } 92 | 93 | public long getDate() 94 | { 95 | return date; 96 | } 97 | 98 | public void setDate(long date) 99 | { 100 | this.date = date; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/utils/Capabilities.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.utils; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.google.firebase.auth.FirebaseAuth; 7 | import com.google.firebase.database.DataSnapshot; 8 | import com.google.firebase.database.DatabaseError; 9 | import com.google.firebase.database.DatabaseReference; 10 | import com.google.firebase.database.FirebaseDatabase; 11 | import com.google.firebase.database.ServerValue; 12 | import com.google.firebase.database.ValueEventListener; 13 | import com.squareup.picasso.OkHttpDownloader; 14 | import com.squareup.picasso.Picasso; 15 | 16 | /** 17 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 18 | * Licensed under Apache License 2.0 19 | * 20 | * @author Raf (https://github.com/h01d) 21 | * @version 1.1 22 | * @since 27/02/2018 23 | */ 24 | 25 | public class Capabilities extends Application 26 | { 27 | private final String TAG = "CA/Capabilities"; 28 | 29 | @Override 30 | public void onCreate() 31 | { 32 | super.onCreate(); 33 | 34 | // For offline use 35 | 36 | FirebaseDatabase.getInstance().setPersistenceEnabled(true); 37 | 38 | Picasso.Builder builder = new Picasso.Builder(this); 39 | builder.downloader(new OkHttpDownloader(this, Integer.MAX_VALUE)); 40 | 41 | Picasso build = builder.build(); 42 | build.setLoggingEnabled(true); 43 | Picasso.setSingletonInstance(build); 44 | 45 | // If user disconnect 46 | 47 | if(FirebaseAuth.getInstance().getCurrentUser() != null) 48 | { 49 | final DatabaseReference userDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()); 50 | userDatabase.addValueEventListener(new ValueEventListener() 51 | { 52 | @Override 53 | public void onDataChange(DataSnapshot dataSnapshot) 54 | { 55 | if(dataSnapshot != null) 56 | { 57 | userDatabase.child("online").onDisconnect().setValue(ServerValue.TIMESTAMP); 58 | } 59 | } 60 | 61 | @Override 62 | public void onCancelled(DatabaseError databaseError) 63 | { 64 | Log.d(TAG, "usersDatabase failed: " + databaseError.getMessage()); 65 | } 66 | }); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/utils/FirebaseMessagingService.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.utils; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationChannel; 5 | import android.app.NotificationManager; 6 | import android.app.PendingIntent; 7 | import android.content.Intent; 8 | import android.graphics.BitmapFactory; 9 | import android.os.Build; 10 | import android.support.v4.app.NotificationCompat; 11 | 12 | import com.github.h01d.chatapp.R; 13 | import com.github.h01d.chatapp.activities.ChatActivity; 14 | import com.google.firebase.messaging.RemoteMessage; 15 | 16 | /** 17 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 18 | * Licensed under Apache License 2.0 19 | * 20 | * @author Raf (https://github.com/h01d) 21 | * @version 1.1 22 | * @since 27/02/2018 23 | */ 24 | 25 | public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService 26 | { 27 | @Override 28 | public void onMessageReceived(RemoteMessage remoteMessage) 29 | { 30 | super.onMessageReceived(remoteMessage); 31 | 32 | // Notification data 33 | 34 | NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 35 | 36 | if(Build.VERSION.SDK_INT >= 26) 37 | { 38 | // API 26+ is required to provide a channel Id 39 | 40 | NotificationChannel notificationChannel = new NotificationChannel(getString(R.string.default_notification_channel_id), "My Notifications", NotificationManager.IMPORTANCE_HIGH); 41 | 42 | notificationChannel.setDescription("Channel description"); 43 | notificationChannel.enableLights(true); 44 | notificationChannel.setLightColor(Notification.DEFAULT_LIGHTS); 45 | notificationChannel.setVibrationPattern(new long[]{0, 100, 100, 100, 100, 100}); 46 | notificationChannel.enableVibration(true); 47 | notificationManager.createNotificationChannel(notificationChannel); 48 | } 49 | 50 | String notificationTitle = remoteMessage.getData().get("title"); 51 | String notificationMessage = remoteMessage.getData().get("body"); 52 | String notificationAction = remoteMessage.getData().get("click_action"); 53 | String notificationFrom = remoteMessage.getData().get("from_user_id"); 54 | 55 | if(notificationTitle.equals("You have a new Message")) 56 | { 57 | // If it's a message notification 58 | // Checking if ChatActivity is not open or if its, it should have a different userId from current 59 | 60 | if(!ChatActivity.running || ChatActivity.running && !ChatActivity.otherUserId.equals(notificationFrom)) 61 | { 62 | // Creating the notification 63 | 64 | NotificationCompat.Builder notification = new NotificationCompat.Builder(this, getString(R.string.default_notification_channel_id)) 65 | .setContentTitle(notificationTitle) 66 | .setContentText(notificationMessage) 67 | .setSmallIcon(R.drawable.logo) 68 | .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), R.mipmap.ic_launcher_round)) 69 | .setAutoCancel(true) 70 | .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE); 71 | 72 | Intent intent = new Intent(notificationAction); 73 | intent.putExtra("userid", notificationFrom); 74 | 75 | // Extract a unique notification from sender userId so we can have only 1 notification per user 76 | 77 | int notificationId = Integer.parseInt(notificationFrom.replaceAll("[^0-9]", "")); 78 | 79 | PendingIntent pendingIntent = PendingIntent.getActivity(this, notificationId % 65535, intent, PendingIntent.FLAG_ONE_SHOT); 80 | 81 | notification.setContentIntent(pendingIntent); 82 | 83 | // Pushing notification to device 84 | 85 | notificationManager.notify(notificationId % 65535, notification.build()); 86 | } 87 | } 88 | else if(notificationTitle.equals("You have a Friend Request")) 89 | { 90 | // If it's friend request notification 91 | 92 | // Creating the notification 93 | 94 | NotificationCompat.Builder notification = new NotificationCompat.Builder(this, getString(R.string.default_notification_channel_id)) 95 | .setContentTitle(notificationTitle) 96 | .setContentText(notificationMessage) 97 | .setSmallIcon(R.drawable.ic_person_add_white_24dp) 98 | .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), R.mipmap.ic_launcher_round)) 99 | .setAutoCancel(true) 100 | .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE); 101 | 102 | Intent intent = new Intent(notificationAction); 103 | intent.putExtra("userid", notificationFrom); 104 | 105 | // Extract a unique notification from sender userId so we can have only 1 notification per user 106 | 107 | int notificationId = Integer.parseInt(notificationFrom.replaceAll("[^0-9]", "")); 108 | 109 | // Adding +1 to notification Id se we can have a Friend Request and a Message Notification at the same time 110 | 111 | PendingIntent pendingIntent = PendingIntent.getActivity(this, notificationId + 1 % 65535, intent, PendingIntent.FLAG_ONE_SHOT); 112 | 113 | notification.setContentIntent(pendingIntent); 114 | 115 | // Pushing notification to device 116 | 117 | notificationManager.notify(notificationId + 1 % 65535, notification.build()); 118 | } 119 | else if(notificationTitle.equals("You have a new friend")) 120 | { 121 | // If it's a new friend 122 | 123 | // Creating the notification 124 | 125 | NotificationCompat.Builder notification = new NotificationCompat.Builder(this, getString(R.string.default_notification_channel_id)) 126 | .setContentTitle(notificationTitle) 127 | .setContentText(notificationMessage) 128 | .setSmallIcon(R.drawable.ic_person_add_white_24dp) 129 | .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), R.mipmap.ic_launcher_round)) 130 | .setAutoCancel(true) 131 | .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE); 132 | 133 | Intent intent = new Intent(notificationAction); 134 | intent.putExtra("userid", notificationFrom); 135 | 136 | // Extract a unique notification from sender userId so we can have only 1 notification per user 137 | 138 | int notificationId = Integer.parseInt(notificationFrom.replaceAll("[^0-9]", "")); 139 | 140 | // Adding +2 to notification Id se we can have a all notifications at the same time 141 | 142 | PendingIntent pendingIntent = PendingIntent.getActivity(this, notificationId + 2 % 65535, intent, PendingIntent.FLAG_ONE_SHOT); 143 | 144 | notification.setContentIntent(pendingIntent); 145 | 146 | // Pushing notification to device 147 | 148 | notificationManager.notify(notificationId + 2 % 65535, notification.build()); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/h01d/chatapp/utils/TouchImageView.java: -------------------------------------------------------------------------------- 1 | package com.github.h01d.chatapp.utils; 2 | 3 | /** 4 | * This is a part of ChatApp Project (https://github.com/h01d/ChatApp) 5 | * Licensed under Apache License 2.0 6 | * 7 | * @author Raf (https://github.com/h01d) 8 | * @version 1.1 9 | * @since 27/02/2018 10 | */ 11 | 12 | import android.content.Context; 13 | import android.graphics.Matrix; 14 | import android.graphics.PointF; 15 | import android.graphics.drawable.Drawable; 16 | import android.util.AttributeSet; 17 | import android.view.MotionEvent; 18 | import android.view.ScaleGestureDetector; 19 | import android.view.View; 20 | 21 | public class TouchImageView extends android.support.v7.widget.AppCompatImageView 22 | { 23 | Matrix matrix; 24 | 25 | static final int NONE = 0; 26 | static final int DRAG = 1; 27 | static final int ZOOM = 2; 28 | int mode = NONE; 29 | 30 | PointF last = new PointF(); 31 | PointF start = new PointF(); 32 | 33 | float minScale = 1f; 34 | float maxScale = 3f; 35 | float[] m; 36 | 37 | int viewWidth, viewHeight; 38 | static final int CLICK = 3; 39 | float saveScale = 1f; 40 | protected float origWidth, origHeight; 41 | int oldMeasuredWidth, oldMeasuredHeight; 42 | 43 | ScaleGestureDetector mScaleDetector; 44 | 45 | Context context; 46 | 47 | public TouchImageView(Context context) 48 | { 49 | super(context); 50 | sharedConstructing(context); 51 | } 52 | 53 | public TouchImageView(Context context, AttributeSet attrs) 54 | { 55 | super(context, attrs); 56 | sharedConstructing(context); 57 | } 58 | 59 | private void stopInterceptEvent() 60 | { 61 | getParent().requestDisallowInterceptTouchEvent(true); 62 | } 63 | 64 | private void startInterceptEvent() 65 | { 66 | getParent().requestDisallowInterceptTouchEvent(false); 67 | } 68 | 69 | private void sharedConstructing(Context context) 70 | { 71 | super.setClickable(true); 72 | 73 | this.context = context; 74 | mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 75 | matrix = new Matrix(); 76 | m = new float[9]; 77 | 78 | setImageMatrix(matrix); 79 | setScaleType(ScaleType.MATRIX); 80 | 81 | setOnTouchListener(new OnTouchListener() 82 | { 83 | 84 | @Override 85 | public boolean onTouch(View v, MotionEvent event) 86 | { 87 | mScaleDetector.onTouchEvent(event); 88 | PointF curr = new PointF(event.getX(), event.getY()); 89 | 90 | switch(event.getAction()) 91 | { 92 | case MotionEvent.ACTION_DOWN: 93 | last.set(curr); 94 | start.set(last); 95 | mode = DRAG; 96 | 97 | stopInterceptEvent(); 98 | break; 99 | 100 | case MotionEvent.ACTION_MOVE: 101 | if(mode == DRAG) 102 | { 103 | float deltaX = curr.x - last.x; 104 | float deltaY = curr.y - last.y; 105 | float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale); 106 | float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale); 107 | 108 | matrix.postTranslate(fixTransX, fixTransY); 109 | fixTrans(); 110 | last.set(curr.x, curr.y); 111 | 112 | float transX = m[Matrix.MTRANS_X]; 113 | 114 | if((int) (getFixTrans(transX, viewWidth, origWidth * saveScale) + fixTransX) == 0) 115 | { 116 | startInterceptEvent(); 117 | } 118 | else 119 | { 120 | stopInterceptEvent(); 121 | } 122 | } 123 | break; 124 | 125 | case MotionEvent.ACTION_UP: 126 | mode = NONE; 127 | int xDiff = (int) Math.abs(curr.x - start.x); 128 | int yDiff = (int) Math.abs(curr.y - start.y); 129 | 130 | if(xDiff < CLICK && yDiff < CLICK) 131 | { 132 | performClick(); 133 | } 134 | 135 | startInterceptEvent(); 136 | break; 137 | 138 | case MotionEvent.ACTION_POINTER_UP: 139 | mode = NONE; 140 | break; 141 | } 142 | 143 | setImageMatrix(matrix); 144 | invalidate(); 145 | return true; 146 | } 147 | 148 | }); 149 | } 150 | 151 | public void setMaxZoom(float x) 152 | { 153 | maxScale = x; 154 | } 155 | 156 | private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener 157 | { 158 | @Override 159 | public boolean onScaleBegin(ScaleGestureDetector detector) 160 | { 161 | mode = ZOOM; 162 | return true; 163 | } 164 | 165 | @Override 166 | public boolean onScale(ScaleGestureDetector detector) 167 | { 168 | float mScaleFactor = detector.getScaleFactor(); 169 | float origScale = saveScale; 170 | 171 | saveScale *= mScaleFactor; 172 | 173 | if(saveScale > maxScale) 174 | { 175 | saveScale = maxScale; 176 | mScaleFactor = maxScale / origScale; 177 | } 178 | else if(saveScale < minScale) 179 | { 180 | saveScale = minScale; 181 | mScaleFactor = minScale / origScale; 182 | } 183 | 184 | if(origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight) 185 | { 186 | matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2); 187 | } 188 | else 189 | { 190 | matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY()); 191 | } 192 | 193 | fixTrans(); 194 | 195 | return true; 196 | } 197 | } 198 | 199 | void fixTrans() 200 | { 201 | matrix.getValues(m); 202 | 203 | float transX = m[Matrix.MTRANS_X]; 204 | float transY = m[Matrix.MTRANS_Y]; 205 | 206 | float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale); 207 | float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale); 208 | 209 | if(fixTransX != 0 || fixTransY != 0) 210 | { 211 | matrix.postTranslate(fixTransX, fixTransY); 212 | } 213 | } 214 | 215 | float getFixTrans(float trans, float viewSize, float contentSize) 216 | { 217 | float minTrans, maxTrans; 218 | 219 | if(contentSize <= viewSize) 220 | { 221 | minTrans = 0; 222 | maxTrans = viewSize - contentSize; 223 | } 224 | else 225 | { 226 | minTrans = viewSize - contentSize; 227 | maxTrans = 0; 228 | } 229 | 230 | if(trans < minTrans) 231 | { 232 | return -trans + minTrans; 233 | } 234 | 235 | if(trans > maxTrans) 236 | { 237 | return -trans + maxTrans; 238 | } 239 | 240 | return 0; 241 | } 242 | 243 | float getFixDragTrans(float delta, float viewSize, float contentSize) 244 | { 245 | if(contentSize <= viewSize) 246 | { 247 | return 0; 248 | } 249 | 250 | return delta; 251 | } 252 | 253 | @Override 254 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 255 | { 256 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 257 | viewWidth = MeasureSpec.getSize(widthMeasureSpec); 258 | viewHeight = MeasureSpec.getSize(heightMeasureSpec); 259 | 260 | if(oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight || viewWidth == 0 || viewHeight == 0) 261 | { 262 | return; 263 | } 264 | 265 | oldMeasuredHeight = viewHeight; 266 | oldMeasuredWidth = viewWidth; 267 | 268 | if(saveScale == 1) 269 | { 270 | float scale; 271 | 272 | Drawable drawable = getDrawable(); 273 | 274 | if(drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) 275 | { 276 | return; 277 | } 278 | 279 | int bmWidth = drawable.getIntrinsicWidth(); 280 | int bmHeight = drawable.getIntrinsicHeight(); 281 | 282 | float scaleX = (float) viewWidth / (float) bmWidth; 283 | float scaleY = (float) viewHeight / (float) bmHeight; 284 | 285 | scale = Math.min(scaleX, scaleY); 286 | matrix.setScale(scale, scale); 287 | 288 | float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight); 289 | float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth); 290 | 291 | redundantYSpace /= (float) 2; 292 | redundantXSpace /= (float) 2; 293 | 294 | matrix.postTranslate(redundantXSpace, redundantYSpace); 295 | 296 | origWidth = viewWidth - 2 * redundantXSpace; 297 | origHeight = viewHeight - 2 * redundantYSpace; 298 | 299 | setImageMatrix(matrix); 300 | } 301 | 302 | fixTrans(); 303 | } 304 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 14 | 17 | 20 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_block_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cancel_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_edit_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_email_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_filter_hdr_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_group_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_image_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_image_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_lock_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_message_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person_add_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_remove_circle_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_send_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamraf/ChatApp/6d10109408041905ef344582b7894e8a509e33f7/app/src/main/res/drawable/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/logo_cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamraf/ChatApp/6d10109408041905ef344582b7894e8a509e33f7/app/src/main/res/drawable/logo_cover.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/logo_shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamraf/ChatApp/6d10109408041905ef344582b7894e8a509e33f7/app/src/main/res/drawable/logo_shadow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/message_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamraf/ChatApp/6d10109408041905ef344582b7894e8a509e33f7/app/src/main/res/drawable/user.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_chat.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 20 | 21 | 26 | 27 | 28 | 29 | 38 | 39 | 46 | 47 | 59 | 60 |