├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── blank.yml ├── .gitignore ├── .idea ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml ├── markdown-navigator-enh.xml ├── markdown-navigator.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── _config.yml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── technorapper │ │ └── boiler │ │ └── ui │ │ └── onboarding │ │ └── activity │ │ ├── MainActivityTest.kt │ │ └── UiRunner.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── technorapper │ │ │ └── boiler │ │ │ ├── application │ │ │ └── App.kt │ │ │ ├── constants │ │ │ ├── DatabaseConstants.kt │ │ │ ├── GlobalVariables.kt │ │ │ └── Preferences.java │ │ │ ├── data │ │ │ ├── Result.kt │ │ │ ├── models │ │ │ │ ├── APIResponse.kt │ │ │ │ ├── UserTableModel.kt │ │ │ │ ├── base │ │ │ │ │ └── BaseDaoModel.kt │ │ │ │ ├── repo │ │ │ │ │ ├── License.kt │ │ │ │ │ ├── Owner.kt │ │ │ │ │ ├── UserRepo.kt │ │ │ │ │ └── UserRepoItem.kt │ │ │ │ └── response │ │ │ │ │ ├── BasicResponse.kt │ │ │ │ │ ├── Result.kt │ │ │ │ │ └── socket │ │ │ │ │ └── SocketModel.kt │ │ │ ├── repository │ │ │ │ ├── BaseRepository.kt │ │ │ │ └── MainActivityRepository.kt │ │ │ ├── tunnel │ │ │ │ ├── database │ │ │ │ │ ├── TechnorapperDriverDatabase.kt │ │ │ │ │ └── dao │ │ │ │ │ │ └── UserMasterDao.kt │ │ │ │ └── remote │ │ │ │ │ ├── ASynchronousSocket.kt │ │ │ │ │ ├── SynchronousApi.kt │ │ │ │ │ └── utils │ │ │ │ │ ├── AppSocket.kt │ │ │ │ │ ├── CheckInternetConnection.kt │ │ │ │ │ └── CustomObserable.kt │ │ │ └── utils │ │ │ │ ├── RoomConverters.kt │ │ │ │ └── TimestampConverter.kt │ │ │ ├── di │ │ │ ├── AppInjector.kt │ │ │ ├── AppSocketServiceFactory.kt │ │ │ ├── WDViewModelFactory.kt │ │ │ ├── builder │ │ │ │ ├── ActivityBuilder.kt │ │ │ │ ├── BroadcastBuilder.kt │ │ │ │ ├── FragmentBuilder.kt │ │ │ │ └── ServiceBuilder.kt │ │ │ ├── component │ │ │ │ ├── AppComponent.kt │ │ │ │ └── BindingComponent.kt │ │ │ ├── module │ │ │ │ ├── AppModule.kt │ │ │ │ ├── BindingModule.kt │ │ │ │ ├── DataBaseModule.kt │ │ │ │ ├── NetworkModule.kt │ │ │ │ ├── PreferenceModule.kt │ │ │ │ ├── ViewModelKey.kt │ │ │ │ └── ViewModelModule.kt │ │ │ └── scopes │ │ │ │ ├── ActivityScoped.kt │ │ │ │ ├── ApplicationScoped.kt │ │ │ │ ├── BindingScoped.kt │ │ │ │ └── RandomUserApplicationScope.kt │ │ │ ├── global │ │ │ └── CustomBindingAdapter.kt │ │ │ ├── helpers │ │ │ └── AppPrefs.kt │ │ │ └── ui │ │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ └── BaseViewModel.kt │ │ │ └── onboarding │ │ │ └── activity │ │ │ ├── MainActivity.kt │ │ │ └── MainActivityViewModel.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── network_security_config.xml │ └── test │ └── java │ └── com │ └── technorapper │ └── boiler │ └── ui │ └── onboarding │ └── activity │ ├── MainActivityDaggerTest.kt │ ├── TestApp.kt │ ├── TestAppComponent.kt │ ├── TestApplicationModule.kt │ └── TestPrefModule.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/blank.yml: -------------------------------------------------------------------------------- 1 | name: Android_CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | jobs: 8 | build: 9 | 10 | runs-on: macos-latest 11 | strategy: 12 | matrix: 13 | api-level: [28] 14 | target: [default, google_apis] 15 | steps: 16 | - uses: actions/checkout@v1 17 | - name: set up JDK 1.8 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: 1.8 21 | - name: Build with Gradle 22 | run: bash ./gradlew assembleDebug --stacktrace 23 | 24 | - name: Upload APK 25 | uses: actions/upload-artifact@v1 26 | with: 27 | name: app 28 | path: app/build/outputs/apk/debug/app-debug.apk 29 | 30 | - name: checkout 31 | uses: actions/checkout@v2 32 | 33 | - uses: reactivecircus/android-emulator-runner@v2 34 | with: 35 | api-level: ${{ matrix.api-level }} 36 | target: ${{ matrix.target }} 37 | arch: x86_64 38 | profile: Nexus 6 39 | script: ./gradlew connectedCheck --stacktrace 40 | 41 | - name: TEST 2 42 | run: ./gradlew test --stacktrace 43 | 44 | 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | exampleForDriver -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | xmlns:android 17 | 18 | ^$ 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 | xmlns:.* 28 | 29 | ^$ 30 | 31 | 32 | BY_NAME 33 | 34 |
35 |
36 | 37 | 38 | 39 | .*:id 40 | 41 | http://schemas.android.com/apk/res/android 42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | .*:name 51 | 52 | http://schemas.android.com/apk/res/android 53 | 54 | 55 | 56 |
57 |
58 | 59 | 60 | 61 | name 62 | 63 | ^$ 64 | 65 | 66 | 67 |
68 |
69 | 70 | 71 | 72 | style 73 | 74 | ^$ 75 | 76 | 77 | 78 |
79 |
80 | 81 | 82 | 83 | .* 84 | 85 | ^$ 86 | 87 | 88 | BY_NAME 89 | 90 |
91 |
92 | 93 | 94 | 95 | .* 96 | 97 | http://schemas.android.com/apk/res/android 98 | 99 | 100 | ANDROID_ATTRIBUTE_ORDER 101 | 102 |
103 |
104 | 105 | 106 | 107 | .* 108 | 109 | .* 110 | 111 | 112 | BY_NAME 113 | 114 |
115 |
116 |
117 |
118 | 119 | 121 |
122 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /.idea/markdown-navigator-enh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /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 [yyyy] [name of copyright owner] 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Base for Android Application using Kotlin 2 | 3 | An enterprise-level Kotlin MVVM boilerplate to help you start building Android applications in minutes. 4 | 5 | ## Overview 6 | 7 | This repository is designed to provide developers with a clean and scalable base for starting new Android applications using Kotlin. The project utilizes modern architecture patterns and libraries, enabling rapid development while ensuring maintainability and scalability. 8 | 9 | ## Architecture 10 | 11 | This project follows the **MVVM (Model-View-ViewModel)** architecture pattern, offering separation of concerns, easier testing, and the use of Jetpack components. 12 | 13 | MVVM Architecture 14 | 15 | --- 16 | 17 | ## Technology Stack 18 | 19 | This boilerplate includes an extensive technology stack that allows developers to build robust and scalable Android applications. 20 | 21 | ### Language 22 | 23 | - **Kotlin 1.3.61** - Modern programming language designed to improve productivity, readability, and safety. 24 | 25 | ### Architecture 26 | 27 | - **MVVM** - Ensures separation of business logic from UI components, promoting maintainability and testability. 28 | 29 | ### Database Layer 30 | 31 | - **Room 2.2.2** - A persistence library that provides an abstraction layer over SQLite, making database operations more manageable. 32 | 33 | ### Dependency Injection 34 | 35 | - **Dagger 2.15** - A powerful dependency injection framework to help manage and inject dependencies efficiently. 36 | 37 | ### Flow Management 38 | 39 | - **Coroutines 1.2.1** - Provides asynchronous programming capabilities with a simpler and more efficient API. 40 | - **RxKotlin 2.4.0** - Reactive programming support for Kotlin. 41 | - **RxAndroid 2.1.1** - Reactive Extensions for Android’s main UI thread. 42 | - **Lifecycle 2.1.0** - Android architecture component to handle lifecycle-aware components. 43 | 44 | ### Networking 45 | 46 | - **Retrofit 2.7.0** - A type-safe HTTP client for Android and Java. 47 | - **OkHttp 3.12.0** - An efficient HTTP client that supports HTTP/2, connection pooling, and more. 48 | - **Socket.IO 0.6.0** - Provides real-time, bidirectional communication between the client and server. 49 | 50 | ### Image Loading & Processing 51 | 52 | - **Picasso 2.71828** - A powerful image downloading and caching library for Android, making image handling simple and efficient. 53 | 54 | ### JSON Parsing 55 | 56 | - **Moshi 1.9.0** - A modern JSON library for Android and Java, offering full support for Kotlin. 57 | - **Gson 2.8.6** - A widely used library for converting JSON to Java objects and vice versa. 58 | 59 | ### Logging 60 | 61 | - **Timber 4.7.1** - A flexible logging library, improving upon Android's default `Log` class. 62 | 63 | ### UI Testing 64 | 65 | - **Espresso** - A UI testing framework that allows you to simulate user interactions and test your application's behavior. 66 | 67 | --- 68 | 69 | ## Getting Started 70 | 71 | Follow these instructions to get a copy of the project up and running on your local machine for development and testing purposes. 72 | 73 | ### Prerequisites 74 | 75 | - Android Studio (latest version) 76 | - Gradle (as per Android Studio configuration) 77 | - Android SDK (API 21 or higher) 78 | 79 | ### Setup 80 | 81 | 1. **Clone the repository:** 82 | ```bash 83 | git clone https://github.com/HaneetGH/KotlinAndroidBase.git 84 | ``` 85 | 86 | 2. **Open in Android Studio:** 87 | Open the project in Android Studio, then allow it to sync the Gradle files. 88 | 89 | 3. **Build the project:** 90 | Build the project to download dependencies and set up the development environment. 91 | 92 | 4. **Run the app:** 93 | Use an emulator or a physical device to run the app directly from Android Studio. 94 | 95 | --- 96 | 97 | ## Contributing 98 | 99 | We welcome contributions from the community! Here's how you can get involved: 100 | 101 | 1. **Fork the repository** by clicking the "Fork" button at the top of the page. 102 | 2. **Create a new branch** for your feature or bug fix: 103 | ```bash 104 | git checkout -b feature-branch 105 | ``` 106 | 3. **Make your changes** and commit them: 107 | ```bash 108 | git commit -m "Description of the feature or fix" 109 | ``` 110 | 4. **Push your branch** to GitHub: 111 | ```bash 112 | git push origin feature-branch 113 | ``` 114 | 5. **Create a Pull Request** to the `main` branch of this repository. 115 | 116 | Please ensure that your code follows the project's standards and includes appropriate tests. 117 | 118 | --- 119 | 120 | ## License 121 | 122 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 123 | 124 | --- 125 | 126 | ## Contact 127 | 128 | For any questions or support, feel free to open an issue on GitHub. 129 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | apply plugin: 'kotlin-kapt' 8 | android { 9 | dataBinding { 10 | enabled = true 11 | } 12 | testOptions { 13 | unitTests.returnDefaultValues = true 14 | } 15 | compileSdkVersion 29 16 | buildToolsVersion "29.0.2" 17 | defaultConfig { 18 | // Enabling multidex support. 19 | multiDexEnabled true 20 | applicationId "com.technorapper.boiler" 21 | minSdkVersion 15 22 | targetSdkVersion 29 23 | versionCode 1 24 | versionName "1.0" 25 | testInstrumentationRunner "com.technorapper.boiler.ui.onboarding.activity.UiRunner" 26 | } 27 | buildTypes { 28 | release { 29 | minifyEnabled false 30 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 31 | } 32 | } 33 | 34 | /* I use Data Binding and Java8 in all my projects */ 35 | dataBinding { 36 | enabled true 37 | } 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_1_8 40 | targetCompatibility JavaVersion.VERSION_1_8 41 | } 42 | } 43 | 44 | kapt { 45 | generateStubs = true 46 | } 47 | dependencies { 48 | implementation fileTree(dir: 'libs', include: ['*.jar']) 49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 50 | implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.61" 51 | implementation 'androidx.appcompat:appcompat:1.1.0' 52 | implementation 'androidx.core:core-ktx:1.1.0' 53 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 54 | testImplementation 'junit:junit:4.12' 55 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 56 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 57 | 58 | 59 | kapt 'com.android.databinding:compiler:3.1.4' 60 | implementation("io.reactivex.rxjava2:rxkotlin:2.4.0") 61 | 62 | //dagger 63 | 64 | kapt "com.google.dagger:dagger-compiler:$daggerVersion" 65 | kapt "com.google.dagger:dagger-android-processor:$daggerVersion" 66 | implementation "com.google.dagger:dagger:$daggerVersion" 67 | implementation "com.google.dagger:dagger-android:$daggerVersion" 68 | implementation "com.google.dagger:dagger-android-support:$daggerVersion" 69 | kapt "com.google.dagger:dagger-compiler:$daggerVersion" 70 | kaptTest "com.google.dagger:dagger-compiler: $daggerVersion" 71 | 72 | 73 | 74 | implementation 'com.squareup.picasso:picasso:2.71828' 75 | implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.0.2' 76 | //room 77 | implementation "androidx.room:room-runtime:$room_version" 78 | implementation "androidx.legacy:legacy-support-v4:1.0.0" 79 | implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle" 80 | implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle" 81 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle" 82 | 83 | // mockk 84 | testImplementation 'io.mockk:mockk:' + mockKVersion 85 | // mockito 86 | testImplementation 'org.mockito:mockito-core:2.19.0' 87 | kapt "androidx.room:room-compiler:$room_version" 88 | 89 | implementation 'com.google.code.gson:gson:2.8.6' 90 | 91 | //Moshi 92 | implementation "com.squareup.moshi:moshi-kotlin:$moshiVersion" 93 | kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" 94 | implementation "com.squareup.retrofit2:converter-moshi:$retrofit2_version" 95 | 96 | //Retrofit2 97 | implementation "com.squareup.retrofit2:retrofit:$retrofit2_version" 98 | implementation "com.squareup.retrofit2:converter-gson:$retrofit2_version" 99 | implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit2_version" 100 | 101 | implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2" 102 | 103 | //Okhttp3 104 | implementation "com.squareup.okhttp3:okhttp:$okhttp3_version" 105 | implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' 106 | 107 | //Picasso for Image Loading 108 | implementation ("com.squareup.picasso:picasso:$picassoVersion"){ 109 | exclude group: "com.android.support" 110 | } 111 | 112 | //Kotlin Coroutines 113 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutineVersion" 114 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutineVersion" 115 | 116 | implementation 'com.jakewharton.timber:timber:4.7.1' 117 | 118 | implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1' 119 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' 120 | implementation('com.github.nkzawa:socket.io-client:0.6.0', { 121 | exclude group: 'org.json', module: 'json' 122 | }) 123 | implementation 'com.android.support:multidex:1.0.0' 124 | androidTestImplementation('com.squareup.okhttp3:mockwebserver:' + rootProject.mockWebServerVersion) { 125 | exclude group: "com.squareup.okhttp3" 126 | } 127 | implementation "com.squareup.retrofit2:converter-scalars:$retrofit2_version" 128 | androidTestImplementation 'androidx.test.ext:junit:1.1.0' , 129 | 'androidx.test:rules:1.1.0', 130 | 'androidx.test.espresso:espresso-core:3.1.1' 131 | 132 | } 133 | -------------------------------------------------------------------------------- /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/technorapper/boiler/ui/onboarding/activity/MainActivityTest.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.ui.onboarding.activity 2 | 3 | import androidx.test.core.app.ActivityScenario 4 | import androidx.test.espresso.Espresso 5 | import androidx.test.espresso.assertion.ViewAssertions 6 | import androidx.test.espresso.matcher.ViewMatchers 7 | import androidx.test.espresso.matcher.ViewMatchers.withId 8 | import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner 9 | import com.technorapper.boiler.R 10 | import org.junit.Assert.* 11 | import org.junit.Test 12 | import org.junit.runner.RunWith 13 | 14 | @RunWith(AndroidJUnit4ClassRunner::class) 15 | class MainActivityTest{ 16 | 17 | @Test 18 | fun testActivity_inView() { 19 | val activityScenario = ActivityScenario.launch(MainActivity::class.java) 20 | 21 | Espresso.onView(withId(R.id.main)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 22 | Espresso.onView(withId(R.id.button)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/technorapper/boiler/ui/onboarding/activity/UiRunner.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.ui.onboarding.activity 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.os.Bundle 6 | import androidx.test.runner.AndroidJUnitRunner 7 | import android.os.StrictMode 8 | import com.technorapper.boiler.application.App 9 | 10 | open class UiRunner : AndroidJUnitRunner() { 11 | 12 | override fun onCreate(arguments: Bundle?) { 13 | StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().permitAll().build()) 14 | super.onCreate(arguments) 15 | } 16 | 17 | override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application { 18 | return super.newApplication(cl, App::class.java.name, context) 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/application/App.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("DEPRECATION") 2 | 3 | package com.technorapper.boiler.application 4 | 5 | import android.app.Activity 6 | import android.app.Application 7 | import androidx.databinding.DataBindingUtil 8 | import androidx.fragment.app.Fragment 9 | import androidx.multidex.MultiDexApplication 10 | import com.technorapper.boiler.di.applyAutoInjector 11 | import com.technorapper.boiler.data.tunnel.remote.utils.AppSocket 12 | import com.technorapper.boiler.di.component.AppComponent 13 | import com.technorapper.boiler.di.component.BindingComponent 14 | import com.technorapper.boiler.di.component.DaggerAppComponent 15 | import com.technorapper.boiler.di.component.DaggerBindingComponent 16 | import com.technorapper.boiler.di.module.AppModule 17 | import com.technorapper.boiler.di.module.BindingModule 18 | import com.github.nkzawa.socketio.client.Socket 19 | import com.squareup.picasso.Picasso 20 | import dagger.android.AndroidInjector 21 | import dagger.android.DispatchingAndroidInjector 22 | import dagger.android.HasActivityInjector 23 | import dagger.android.support.HasSupportFragmentInjector 24 | import javax.inject.Inject 25 | 26 | open class App : Application(), HasActivityInjector, HasSupportFragmentInjector { 27 | @Inject 28 | lateinit var appComponent: AppComponent 29 | 30 | @Inject 31 | lateinit var picasso: Picasso 32 | 33 | @Inject 34 | lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector 35 | 36 | @Inject 37 | lateinit var dispatchingFragmentInjector: DispatchingAndroidInjector 38 | @Inject 39 | lateinit var socket: AppSocket 40 | 41 | override fun onCreate() { 42 | super.onCreate() 43 | INSTANCE = this 44 | initDagger() 45 | socket.connect() 46 | val bindingComponent: BindingComponent? = DaggerBindingComponent.builder() 47 | ?.bindingModule(BindingModule()) 48 | ?.picasso(picasso) 49 | ?.build() 50 | DataBindingUtil.setDefaultComponent(bindingComponent) 51 | applyAutoInjector() 52 | } 53 | 54 | open fun initDagger() { 55 | appComponent = DaggerAppComponent.builder() 56 | .application(this) 57 | .appModule(AppModule(this)) 58 | .build() 59 | appComponent.inject(this) 60 | } 61 | 62 | override fun activityInjector(): DispatchingAndroidInjector? { 63 | return dispatchingAndroidInjector 64 | } 65 | 66 | override fun supportFragmentInjector(): AndroidInjector? { 67 | return dispatchingFragmentInjector 68 | } 69 | 70 | companion object { 71 | lateinit var INSTANCE: App 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/constants/DatabaseConstants.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.constants 2 | 3 | class DatabaseConstants { 4 | companion object { 5 | val DB_NAME = "example_driver.db" 6 | } 7 | 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/constants/GlobalVariables.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.constants 2 | 3 | class GlobalVariables { 4 | companion object { 5 | const val TIME_STAMP_FORMAT = "yyyy-MM-dd HH:mm:ss.SSSSS" 6 | const val SOCKET_URL = "http://b03f8515.ngrok.io" 7 | } 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/constants/Preferences.java: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.constants; 2 | 3 | public interface Preferences { 4 | String APP_NAME = "app_name"; 5 | String IS_LOGIN = "is_login"; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/Result.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data 2 | 3 | sealed class Result { 4 | data class Success(val data: T) : Result() 5 | data class Error(val exception: Exception) : Result() 6 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/APIResponse.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class APIResponse( 6 | @SerializedName("current") 7 | val current: Current? = null, 8 | @SerializedName("location") 9 | val location: LocationResponse? = null, 10 | @SerializedName("forecast") 11 | val forecast: ForecastResponse? = null 12 | ) { 13 | override fun toString(): String { 14 | return "APIResponse(current=$current, location=$location, forecast=$forecast)" 15 | } 16 | } 17 | 18 | data class Current( 19 | @SerializedName("temp_c") 20 | val tempC: String? = null, 21 | @SerializedName("temp_f") 22 | val tempF: String? = null, 23 | @SerializedName("humidity") 24 | val humidity: String? = null 25 | ) { 26 | override fun toString(): String { 27 | return "Current(tempC=$tempC, tempF=$tempF, humidity=$humidity)" 28 | } 29 | } 30 | 31 | data class LocationResponse( 32 | @SerializedName("region") 33 | val region: String? = null, 34 | @SerializedName("country") 35 | val country: String? = null, 36 | @SerializedName("localtime") 37 | val localtime: String? = null, 38 | @SerializedName("lon") 39 | val lng: String? = null, 40 | @SerializedName("lat") 41 | val lat: String? = null, 42 | @SerializedName("tz_id") 43 | val tzId: String? = null, 44 | @SerializedName("name") 45 | val name: String? = null 46 | ) { 47 | override fun toString(): String { 48 | return "LocationResponse(region=$region, country=$country, localtime=$localtime, lng=$lng, lat=$lat, tzId=$tzId, name=$name)" 49 | } 50 | } 51 | 52 | data class ForecastResponse( 53 | @SerializedName("forecastday") 54 | val forecastdayArray: ArrayList? = null 55 | ) { 56 | override fun toString(): String { 57 | return "ForecastResponse(forecastdayArray=$forecastdayArray)" 58 | } 59 | } 60 | 61 | data class ForecastDay( 62 | @SerializedName("day") 63 | val day: Day? = null, 64 | @SerializedName("date") 65 | val date: String? = null 66 | ) { 67 | override fun toString(): String { 68 | return "ForecastDay(day=$day, date=$date)" 69 | } 70 | } 71 | 72 | data class Day( 73 | @SerializedName("avgtemp_c") 74 | val avgTempC: Double? = null, 75 | @SerializedName("avghumidity") 76 | val avgHumidity: Int? = null, 77 | @SerializedName("avgtemp_f") 78 | val avgTempF: Double? = null 79 | ) { 80 | override fun toString(): String { 81 | return "Day(avgTempC=$avgTempC, avgHumidity=$avgHumidity, avgTempF=$avgTempF)" 82 | } 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/UserTableModel.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.models 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import com.technorapper.boiler.data.models.base.BaseDaoModel 7 | import com.google.gson.annotations.Expose 8 | import com.google.gson.annotations.SerializedName 9 | 10 | @Entity(tableName = "userMaster" + "") 11 | data class UserTableModel( 12 | @SerializedName("userMaster_id") 13 | @Expose 14 | @PrimaryKey 15 | @ColumnInfo(name = "userMaster_id") 16 | var userMaster_id: String, 17 | 18 | @SerializedName("lat") 19 | @Expose 20 | @ColumnInfo(name = "lat") 21 | val lat: String, 22 | 23 | @SerializedName("lng") 24 | @Expose 25 | @ColumnInfo(name = "lng") 26 | val lng: String, 27 | 28 | @SerializedName("name") 29 | @Expose 30 | @ColumnInfo(name = "name") 31 | var name: String 32 | ) : BaseDaoModel() { 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/base/BaseDaoModel.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.models.base 2 | 3 | import androidx.databinding.BaseObservable 4 | import androidx.room.ColumnInfo 5 | import androidx.room.TypeConverters 6 | import com.technorapper.boiler.data.utils.TimestampConverter 7 | import com.google.gson.annotations.Expose 8 | import java.io.Serializable 9 | import java.util.* 10 | 11 | open class BaseDaoModel() : BaseObservable(), Serializable { 12 | 13 | @Expose 14 | @TypeConverters(TimestampConverter::class) 15 | @ColumnInfo(name = "createdOn") 16 | var createdOn = Date() 17 | @Expose 18 | @TypeConverters(TimestampConverter::class) 19 | @ColumnInfo(name = "updatedOn") 20 | var updatedOn = Date() 21 | @Expose 22 | @ColumnInfo(name = "updatedBy") 23 | var updatedBy = "" 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/repo/License.kt: -------------------------------------------------------------------------------- 1 | package com.bt.technorapper.driver.data.models.github.repo 2 | 3 | data class License( 4 | val key: String, 5 | val name: String, 6 | val node_id: String, 7 | val spdx_id: String, 8 | val url: Any 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/repo/Owner.kt: -------------------------------------------------------------------------------- 1 | package com.bt.technorapper.driver.data.models.github.repo 2 | 3 | data class Owner( 4 | val avatar_url: String, 5 | val events_url: String, 6 | val followers_url: String, 7 | val following_url: String, 8 | val gists_url: String, 9 | val gravatar_id: String, 10 | val html_url: String, 11 | val id: Int, 12 | val login: String, 13 | val node_id: String, 14 | val organizations_url: String, 15 | val received_events_url: String, 16 | val repos_url: String, 17 | val site_admin: Boolean, 18 | val starred_url: String, 19 | val subscriptions_url: String, 20 | val type: String, 21 | val url: String 22 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/repo/UserRepo.kt: -------------------------------------------------------------------------------- 1 | package com.bt.technorapper.driver.data.models.github.repo 2 | 3 | class UserRepo : ArrayList() -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/repo/UserRepoItem.kt: -------------------------------------------------------------------------------- 1 | package com.bt.technorapper.driver.data.models.github.repo 2 | 3 | data class UserRepoItem( 4 | val archive_url: String, 5 | val archived: Boolean, 6 | val assignees_url: String, 7 | val blobs_url: String, 8 | val branches_url: String, 9 | val clone_url: String, 10 | val collaborators_url: String, 11 | val comments_url: String, 12 | val commits_url: String, 13 | val compare_url: String, 14 | val contents_url: String, 15 | val contributors_url: String, 16 | val created_at: String, 17 | val default_branch: String, 18 | val deployments_url: String, 19 | val description: String, 20 | val disabled: Boolean, 21 | val downloads_url: String, 22 | val events_url: String, 23 | val fork: Boolean, 24 | val forks: Int, 25 | val forks_count: Int, 26 | val forks_url: String, 27 | val full_name: String, 28 | val git_commits_url: String, 29 | val git_refs_url: String, 30 | val git_tags_url: String, 31 | val git_url: String, 32 | val has_downloads: Boolean, 33 | val has_issues: Boolean, 34 | val has_pages: Boolean, 35 | val has_projects: Boolean, 36 | val has_wiki: Boolean, 37 | val homepage: String, 38 | val hooks_url: String, 39 | val html_url: String, 40 | val id: Int, 41 | val issue_comment_url: String, 42 | val issue_events_url: String, 43 | val issues_url: String, 44 | val keys_url: String, 45 | val labels_url: String, 46 | val language: String, 47 | val languages_url: String, 48 | val license: License, 49 | val merges_url: String, 50 | val milestones_url: String, 51 | val mirror_url: Any, 52 | val name: String, 53 | val node_id: String, 54 | val notifications_url: String, 55 | val open_issues: Int, 56 | val open_issues_count: Int, 57 | val owner: Owner, 58 | val `private`: Boolean, 59 | val pulls_url: String, 60 | val pushed_at: String, 61 | val releases_url: String, 62 | val size: Int, 63 | val ssh_url: String, 64 | val stargazers_count: Int, 65 | val stargazers_url: String, 66 | val statuses_url: String, 67 | val subscribers_url: String, 68 | val subscription_url: String, 69 | val svn_url: String, 70 | val tags_url: String, 71 | val teams_url: String, 72 | val trees_url: String, 73 | val updated_at: String, 74 | val url: String, 75 | val watchers: Int, 76 | val watchers_count: Int 77 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/response/BasicResponse.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.models.response 2 | 3 | import com.google.gson.annotations.Expose 4 | import com.google.gson.annotations.SerializedName 5 | import com.squareup.moshi.Json 6 | import com.squareup.moshi.JsonClass 7 | 8 | @JsonClass(generateAdapter = true) 9 | data class BasicResponse( 10 | val result: Result 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/response/Result.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.models.response 2 | 3 | import com.google.gson.annotations.Expose 4 | import com.google.gson.annotations.SerializedName 5 | import com.squareup.moshi.Json 6 | import com.squareup.moshi.JsonClass 7 | 8 | @JsonClass(generateAdapter = true) 9 | data class Result(val code: Int, val msg: String) -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/models/response/socket/SocketModel.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.models.response.socket 2 | 3 | class SocketModel { 4 | var type = 0 5 | 6 | var message: String? = null} 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/repository/BaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.repository 2 | 3 | 4 | import android.util.Log 5 | import com.squareup.picasso.Picasso 6 | import retrofit2.Response 7 | import java.io.IOException 8 | 9 | open class BaseRepository { 10 | 11 | suspend fun safeApiCall(call: suspend () -> Response, errorMessage: String): T? { 12 | 13 | val result: com.technorapper.boiler.data.Result = safeApiResult(call, errorMessage) 14 | var data: T? = null 15 | 16 | when (result) { 17 | is com.technorapper.boiler.data.Result.Success -> 18 | data = result.data 19 | is com.technorapper.boiler.data.Result.Error -> { 20 | Log.d("1.DataRepository", "$errorMessage & Exception - ${result.exception}") 21 | } 22 | } 23 | 24 | 25 | return data 26 | 27 | } 28 | 29 | private suspend fun safeApiResult( 30 | call: suspend () -> Response, 31 | errorMessage: String 32 | ): com.technorapper.boiler.data.Result { 33 | val response = call.invoke() 34 | if (response.isSuccessful) return com.technorapper.boiler.data.Result.Success(response.body()!!) 35 | 36 | return com.technorapper.boiler.data.Result.Error(IOException("Error Occurred during getting safe Api result, Custom ERROR - $errorMessage")) 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/repository/MainActivityRepository.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.repository 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.util.Log 6 | import androidx.lifecycle.MutableLiveData 7 | import com.bt.technorapper.driver.data.models.github.repo.UserRepo 8 | import com.google.gson.JsonObject 9 | import com.technorapper.boiler.data.models.response.BasicResponse 10 | import com.technorapper.boiler.data.tunnel.remote.SynchronousApi 11 | import com.technorapper.boiler.di.scopes.ApplicationScoped 12 | import io.reactivex.Flowable 13 | import retrofit2.await 14 | import javax.inject.Inject 15 | 16 | 17 | @ApplicationScoped 18 | class MainActivityRepository @Inject internal constructor( 19 | private val asyncApi: SynchronousApi, 20 | context: Context 21 | ) : BaseRepository() { 22 | 23 | @SuppressLint("TimberArgCount") 24 | suspend fun getAllApi(): MutableLiveData? { 25 | val response = asyncApi.getPosts("782323523", 7)/*safeApiCall( 26 | call = { asyncApi.getPosts("782323523", 7) }, 27 | errorMessage = "Error Fetching Popular Movies")*/ 28 | 29 | if (response != null) { 30 | Log.d("JSON", response.result.toString()) 31 | } 32 | return null 33 | /*withContext(Dispatchers.Main) { 34 | try { 35 | if (response != null) { 36 | 37 | 38 | *//* var json = response.string().toString(); 39 | val moshi = Moshi.Builder().build() 40 | val jsonAdapter: JsonAdapter = 41 | moshi.adapter( 42 | BasicResponse::class.java 43 | ) 44 | 45 | val blackjackHand: BasicResponse? = jsonAdapter.fromJson(json) 46 | System.out.println(blackjackHand)*//* 47 | 48 | //Do something with response e.g show to the UI. 49 | } else { 50 | Timber.d("Error: ${response}") 51 | } 52 | } catch (e: HttpException) { 53 | Timber.e("Exception ${e.message}") 54 | } catch (e: Throwable) { 55 | Timber.e("Ooops: Something else went wrong") 56 | } 57 | 58 | } 59 | }*/ 60 | 61 | 62 | } 63 | 64 | suspend fun getUserRepo(url: String): UserRepo { 65 | return asyncApi.getUserRepo(url) 66 | 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/tunnel/database/TechnorapperDriverDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.tunnel.database 2 | 3 | import androidx.room.Database 4 | import androidx.room.RoomDatabase 5 | import androidx.room.TypeConverters 6 | import com.technorapper.boiler.data.tunnel.database.dao.UserMasterDao 7 | import com.technorapper.boiler.data.models.UserTableModel 8 | import com.technorapper.boiler.data.utils.RoomConverters 9 | 10 | @Database( 11 | entities = arrayOf(UserTableModel::class), 12 | version = 1, 13 | exportSchema = false 14 | ) 15 | @TypeConverters( 16 | RoomConverters::class 17 | ) 18 | abstract class exampleDatabase : RoomDatabase() { 19 | // private static TechnorapperDatabase TechnorapperDB; 20 | 21 | abstract fun getUserMasterDao(): UserMasterDao 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/tunnel/database/dao/UserMasterDao.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.tunnel.database.dao 2 | 3 | import androidx.room.* 4 | import com.technorapper.boiler.data.models.UserTableModel 5 | 6 | @Dao 7 | abstract class UserMasterDao { 8 | 9 | @Query("SELECT * FROM userMaster") 10 | abstract fun getAll(): List 11 | 12 | 13 | 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/tunnel/remote/ASynchronousSocket.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.tunnel.remote 2 | 3 | interface ASynchronousSocket { 4 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/tunnel/remote/SynchronousApi.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.tunnel.remote 2 | 3 | import com.bt.technorapper.driver.data.models.github.repo.UserRepo 4 | import com.technorapper.boiler.data.models.response.BasicResponse 5 | import com.google.gson.JsonObject 6 | import com.technorapper.boiler.data.models.APIResponse 7 | import io.reactivex.Flowable 8 | import kotlinx.coroutines.Deferred 9 | import okhttp3.ResponseBody 10 | import retrofit2.Response 11 | 12 | import retrofit2.http.GET 13 | import retrofit2.http.Path 14 | import retrofit2.http.Query 15 | import retrofit2.http.Url 16 | 17 | interface SynchronousApi { 18 | @GET("GetMonitorNotification") 19 | suspend fun getPosts( 20 | @Query("customer_id") id: String?, @Query( 21 | "job_type" 22 | ) job_type: Int 23 | ): BasicResponse 24 | 25 | @GET 26 | fun getForecastTempWithUrl( 27 | @Url 28 | url: String 29 | ): Flowable 30 | 31 | @GET("users/{name}/repos") 32 | suspend fun getUserRepo( 33 | @Path( 34 | "name" 35 | ) name: String? 36 | ): UserRepo 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/tunnel/remote/utils/AppSocket.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.tunnel.remote.utils 2 | 3 | import com.github.nkzawa.emitter.Emitter 4 | import com.github.nkzawa.socketio.client.Socket 5 | import io.reactivex.Completable 6 | 7 | class AppSocket(private val socket: Socket) { 8 | 9 | private val listeners: MutableList = mutableListOf() 10 | 11 | val isConnected: Boolean get() = socket.connected() 12 | 13 | init { 14 | socket.on(Socket.EVENT_CONNECT, { 15 | listeners.forEach { 16 | it.onConnect(socket) 17 | } 18 | }) 19 | } 20 | 21 | fun connect() { 22 | socket.open() 23 | } 24 | 25 | fun disconnect() { 26 | socket.close() 27 | } 28 | 29 | fun request(name: String, any: Any) { 30 | if (isConnected) 31 | socket.emit(name, any) 32 | 33 | } 34 | 35 | fun on(name: String, listener: Emitter.Listener) { 36 | socket.on(name, listener) 37 | } 38 | 39 | 40 | fun addListener(listener: CommonListener) { 41 | listeners.add(listener) 42 | } 43 | 44 | fun removeListener(listener: CommonListener) { 45 | listeners.iterator().run { 46 | while (hasNext()) 47 | if (next() == listener) 48 | remove() 49 | } 50 | } 51 | 52 | interface CommonListener { 53 | fun onConnect(socket: Socket) 54 | } 55 | } 56 | 57 | 58 | class SocketNotConnectedException : Throwable() -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/tunnel/remote/utils/CheckInternetConnection.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.tunnel.remote.utils 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.net.ConnectivityManager 6 | import java.net.InetAddress 7 | 8 | class CheckInternetConnection(private val _context: Context) { 9 | 10 | fun isNetworkAvailable(): Boolean { /*ConnectivityManager connectivityManager 11 | = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE); 12 | NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();*/ 13 | return isOnline() 14 | } 15 | 16 | companion object { 17 | fun isInternetConnected(context: Context): Boolean { 18 | val connectivityManager = 19 | context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 20 | val activeNetworkInfo = connectivityManager.activeNetworkInfo 21 | return activeNetworkInfo != null && activeNetworkInfo.isConnected 22 | } 23 | } 24 | 25 | fun isOnline(): Boolean { 26 | try { 27 | val p1 = 28 | Runtime.getRuntime().exec("ping -c 1 www.google.com") 29 | val returnVal = p1.waitFor() 30 | return returnVal == 0 31 | } catch (e: Exception) { 32 | e.printStackTrace() 33 | } 34 | return false 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/tunnel/remote/utils/CustomObserable.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.tunnel.remote.utils 2 | 3 | import com.technorapper.boiler.data.models.response.socket.SocketModel 4 | import com.github.nkzawa.socketio.client.IO 5 | import com.github.nkzawa.socketio.client.Socket 6 | import io.reactivex.Observable 7 | import io.reactivex.Observer 8 | import io.reactivex.android.MainThreadDisposable 9 | import java.net.URISyntaxException 10 | import javax.inject.Inject 11 | 12 | class CustomObserable : Observable() { 13 | @Inject 14 | lateinit var socket: AppSocket 15 | 16 | override fun subscribeActual(observer: Observer) { 17 | val listener = Listener(socket, observer) 18 | observer.onSubscribe(listener) 19 | doSocketIoThing(observer) 20 | observer.onComplete() 21 | doSocketIoThing(observer) 22 | } 23 | 24 | private fun doSocketIoThing(observer: Observer) { 25 | 26 | if (socket.isConnected) { 27 | socket.request("news", "example CONNECTED"); 28 | } 29 | } 30 | 31 | internal class Listener( val socket: AppSocket, bserver: Observer?) : MainThreadDisposable() { 32 | 33 | override fun onDispose() { 34 | socket.disconnect() 35 | } 36 | 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/utils/RoomConverters.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.utils 2 | 3 | import androidx.room.TypeConverter 4 | import java.util.* 5 | 6 | class RoomConverters { 7 | @TypeConverter 8 | 9 | fun toDate(timestamp: Long?): Date? { 10 | return timestamp?.let { Date(it) } 11 | } 12 | 13 | @TypeConverter 14 | fun toTimestamp(date: Date?): Long? { 15 | return date?.time 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/data/utils/TimestampConverter.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.data.utils 2 | 3 | import androidx.room.TypeConverter 4 | import com.technorapper.boiler.constants.GlobalVariables 5 | import java.text.DateFormat 6 | import java.text.ParseException 7 | import java.text.SimpleDateFormat 8 | import java.util.* 9 | 10 | object TimestampConverter { 11 | var df: DateFormat = SimpleDateFormat(GlobalVariables.TIME_STAMP_FORMAT) 12 | @TypeConverter 13 | fun fromTimestamp(value: String?): Date? { 14 | return if (value != null) { 15 | try { 16 | return df.parse(value) 17 | } catch (e: ParseException) { 18 | e.printStackTrace() 19 | } 20 | null 21 | } else { 22 | null 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/AppInjector.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.os.Bundle 6 | import androidx.fragment.app.Fragment 7 | import androidx.fragment.app.FragmentActivity 8 | import androidx.fragment.app.FragmentManager 9 | 10 | import dagger.android.AndroidInjection 11 | import dagger.android.support.AndroidSupportInjection 12 | import dagger.android.support.HasSupportFragmentInjector 13 | 14 | 15 | interface Injectable 16 | 17 | fun Application.applyAutoInjector() = registerActivityLifecycleCallbacks( 18 | object : Application.ActivityLifecycleCallbacks { 19 | 20 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { 21 | handleActivity(activity) 22 | } 23 | 24 | override fun onActivityStarted(activity: Activity) { 25 | 26 | } 27 | 28 | override fun onActivityResumed(activity: Activity) { 29 | 30 | } 31 | 32 | override fun onActivityPaused(activity: Activity) { 33 | 34 | } 35 | 36 | override fun onActivityStopped(activity: Activity) { 37 | 38 | } 39 | 40 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) { 41 | 42 | } 43 | 44 | override fun onActivityDestroyed(activity: Activity) { 45 | 46 | } 47 | }) 48 | 49 | private fun handleActivity(activity: Activity) { 50 | if (activity is Injectable || activity is HasSupportFragmentInjector) { 51 | AndroidInjection.inject(activity) 52 | } 53 | if (activity is FragmentActivity) { 54 | activity.supportFragmentManager.registerFragmentLifecycleCallbacks( 55 | object : FragmentManager.FragmentLifecycleCallbacks() { 56 | override fun onFragmentCreated(fm: FragmentManager, f: Fragment, s: Bundle?) { 57 | if (f is Injectable) { 58 | AndroidSupportInjection.inject(f) 59 | } 60 | } 61 | }, true 62 | ) 63 | } 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/AppSocketServiceFactory.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di 2 | 3 | import com.technorapper.boiler.data.tunnel.remote.utils.AppSocket 4 | import com.github.nkzawa.socketio.client.Manager 5 | import java.net.URI 6 | 7 | object AppSocketServiceFactory { 8 | 9 | private val baseSocketUrl: String = "http://b03f8515.ngrok.io" 10 | 11 | private enum class SocketNameSpace(val value: String) { 12 | CARD("/") 13 | } 14 | 15 | private val socketManager: Manager by lazy { 16 | Manager(URI(baseSocketUrl)) 17 | } 18 | 19 | fun exampleSocket() = AppSocket(socketManager.socket(SocketNameSpace.CARD.value)) 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/WDViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import com.technorapper.boiler.di.scopes.ApplicationScoped 6 | import javax.inject.Inject 7 | import javax.inject.Provider 8 | 9 | 10 | class WDViewModelFactory @Inject constructor( 11 | private val creators: Map, @JvmSuppressWildcards Provider> 12 | ) : ViewModelProvider.Factory { 13 | 14 | @Suppress("UNCHECKED_CAST") 15 | override fun create(modelClass: Class): T { 16 | var creator: Provider? = creators[modelClass] 17 | if (creator == null) { 18 | for ((key, value) in creators) { 19 | if (modelClass.isAssignableFrom(key)) { 20 | creator = value 21 | break 22 | } 23 | } 24 | } 25 | if (creator == null) throw IllegalArgumentException("unknown model class " + modelClass) 26 | try { 27 | return creator.get() as T 28 | } catch (e: Exception) { 29 | throw RuntimeException(e) 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/builder/ActivityBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.builder 2 | 3 | import com.technorapper.boiler.ui.onboarding.activity.MainActivity 4 | import com.technorapper.boiler.di.scopes.ActivityScoped 5 | import dagger.Module 6 | 7 | import dagger.android.ContributesAndroidInjector 8 | 9 | @Module 10 | internal abstract class ActivityBuilder { 11 | 12 | @ActivityScoped 13 | @ContributesAndroidInjector 14 | abstract fun bindMainActivity(): MainActivity? 15 | 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/builder/BroadcastBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.builder 2 | 3 | import dagger.Module 4 | 5 | @Module 6 | public abstract class BroadcastBuilder {} -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/builder/FragmentBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.builder 2 | 3 | import dagger.Module 4 | 5 | @Module 6 | public abstract class FragmentBuilder {} -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/builder/ServiceBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.builder 2 | 3 | import dagger.Module 4 | 5 | 6 | @Module 7 | public abstract class ServiceBuilder {} -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/component/AppComponent.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.component 2 | 3 | 4 | import com.technorapper.boiler.application.App 5 | import com.technorapper.boiler.di.builder.ActivityBuilder 6 | import com.technorapper.boiler.di.builder.FragmentBuilder 7 | import com.technorapper.boiler.di.module.* 8 | import com.technorapper.boiler.di.scopes.ApplicationScoped 9 | import dagger.BindsInstance 10 | import dagger.Component 11 | import dagger.android.support.AndroidSupportInjectionModule 12 | 13 | @ApplicationScoped 14 | @Component( 15 | modules = 16 | arrayOf( 17 | AndroidSupportInjectionModule::class, 18 | AppModule::class, 19 | PreferenceModule::class, 20 | DataBaseModule::class, 21 | NetworkModule::class, 22 | ActivityBuilder::class, 23 | FragmentBuilder::class 24 | ) 25 | ) 26 | interface AppComponent { 27 | fun inject(app: App) 28 | 29 | @Component.Builder 30 | interface Builder { 31 | @BindsInstance 32 | fun application(application: App): Builder 33 | 34 | fun appModule(appModule: AppModule): Builder 35 | 36 | fun build(): AppComponent 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/component/BindingComponent.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.component 2 | import androidx.databinding.DataBindingComponent 3 | import com.technorapper.boiler.di.module.BindingModule 4 | import com.technorapper.boiler.di.scopes.BindingScoped 5 | import com.technorapper.boiler.global.CustomBindingAdapter 6 | import com.squareup.picasso.Picasso 7 | import dagger.BindsInstance 8 | import dagger.Component 9 | 10 | @BindingScoped 11 | @Component(modules = arrayOf(BindingModule::class)) 12 | interface BindingComponent : DataBindingComponent { 13 | fun getImageBindingAdapter(): CustomBindingAdapter? 14 | 15 | @Component.Builder 16 | interface Builder { 17 | @BindsInstance 18 | fun picasso(picasso: Picasso): Builder 19 | fun bindingModule(bindingModule: BindingModule): Builder 20 | fun build(): BindingComponent 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/module/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.module 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | 6 | import com.technorapper.boiler.constants.GlobalVariables 7 | import com.technorapper.boiler.data.tunnel.remote.utils.AppSocket 8 | import com.technorapper.boiler.di.AppSocketServiceFactory 9 | import com.technorapper.boiler.di.scopes.ApplicationScoped 10 | import com.github.nkzawa.socketio.client.IO 11 | import com.github.nkzawa.socketio.client.Socket 12 | import com.squareup.picasso.OkHttp3Downloader 13 | import com.squareup.picasso.Picasso 14 | import com.technorapper.boiler.application.App 15 | import com.technorapper.boiler.data.tunnel.remote.utils.CheckInternetConnection 16 | import dagger.Module 17 | import dagger.Provides 18 | import okhttp3.OkHttpClient 19 | import java.net.URISyntaxException 20 | 21 | @Module(includes = [ViewModelModule::class]) 22 | open class AppModule(val application: App) { 23 | 24 | 25 | @Provides 26 | @ApplicationScoped 27 | open fun picasso( 28 | app: App 29 | ): Picasso { 30 | return Picasso.Builder(app.applicationContext).loggingEnabled(true).build() 31 | } 32 | 33 | @Provides 34 | open fun okHttp3Downloader(okHttpClient: OkHttpClient?): OkHttp3Downloader? { 35 | return OkHttp3Downloader(okHttpClient) 36 | } 37 | 38 | 39 | @Provides 40 | @ApplicationScoped 41 | open fun context(): Context { 42 | return application 43 | } 44 | 45 | @Provides 46 | @ApplicationScoped 47 | open fun provideExampleSocket(app: App): AppSocket { 48 | return AppSocketServiceFactory.exampleSocket() 49 | } 50 | 51 | @Provides 52 | @ApplicationScoped 53 | open fun provideInternetConnection(application: Context): CheckInternetConnection? { 54 | return CheckInternetConnection(application) 55 | } 56 | 57 | @Provides 58 | @ApplicationScoped 59 | open fun provideApplication(): Application { 60 | return application 61 | } 62 | 63 | 64 | 65 | 66 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/module/BindingModule.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.module 2 | 3 | import com.technorapper.boiler.di.scopes.BindingScoped 4 | import com.technorapper.boiler.global.CustomBindingAdapter 5 | import com.squareup.picasso.Picasso 6 | 7 | import dagger.Module 8 | import dagger.Provides 9 | 10 | @Module 11 | class BindingModule { 12 | @Provides 13 | @BindingScoped 14 | fun provideImageBindingAdapter(picasso: Picasso): CustomBindingAdapter { 15 | return CustomBindingAdapter(picasso) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/module/DataBaseModule.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.module 2 | 3 | import androidx.room.Room 4 | import com.technorapper.boiler.constants.DatabaseConstants 5 | import com.technorapper.boiler.data.tunnel.database.exampleDatabase 6 | import com.technorapper.boiler.data.tunnel.database.dao.UserMasterDao 7 | 8 | import com.technorapper.boiler.application.App 9 | import com.technorapper.boiler.di.scopes.ApplicationScoped 10 | import dagger.Module 11 | import dagger.Provides 12 | 13 | @Module 14 | class DataBaseModule { 15 | 16 | 17 | @Provides 18 | @ApplicationScoped 19 | fun provideWDDatabase(context: App): exampleDatabase { 20 | return Room.databaseBuilder( 21 | context, 22 | exampleDatabase::class.java, DatabaseConstants.DB_NAME 23 | ).fallbackToDestructiveMigration() /*.allowMainThreadQueries()*/ 24 | .build() 25 | } 26 | 27 | 28 | @ApplicationScoped 29 | @Provides 30 | 31 | fun provideUserMasterDao(exampleDatabase: exampleDatabase): UserMasterDao { 32 | return exampleDatabase.getUserMasterDao() 33 | } 34 | 35 | 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/module/NetworkModule.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.module 2 | 3 | import android.app.Application 4 | import android.os.Build 5 | import androidx.annotation.RequiresApi 6 | import com.franmontiel.persistentcookiejar.PersistentCookieJar 7 | import com.franmontiel.persistentcookiejar.cache.SetCookieCache 8 | import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor 9 | import com.google.gson.FieldNamingPolicy 10 | import com.google.gson.Gson 11 | import com.google.gson.GsonBuilder 12 | import com.squareup.moshi.Moshi 13 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory 14 | import com.technorapper.boiler.data.tunnel.remote.SynchronousApi 15 | import com.technorapper.boiler.data.tunnel.remote.utils.CheckInternetConnection 16 | import com.technorapper.boiler.di.scopes.ApplicationScoped 17 | import com.technorapper.boiler.helpers.AppPrefs 18 | import dagger.Module 19 | import dagger.Provides 20 | import io.reactivex.schedulers.Schedulers 21 | import okhttp3.* 22 | import okhttp3.logging.HttpLoggingInterceptor 23 | import retrofit2.Retrofit 24 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory 25 | import retrofit2.converter.gson.GsonConverterFactory 26 | import retrofit2.converter.moshi.MoshiConverterFactory 27 | import retrofit2.converter.scalars.ScalarsConverterFactory 28 | import java.net.CookieHandler 29 | import java.net.CookieManager 30 | import java.net.CookiePolicy 31 | import java.security.SecureRandom 32 | import java.security.cert.CertificateException 33 | import java.security.cert.X509Certificate 34 | import java.util.concurrent.TimeUnit 35 | import javax.inject.Named 36 | import javax.net.ssl.SSLContext 37 | import javax.net.ssl.TrustManager 38 | import javax.net.ssl.X509TrustManager 39 | 40 | @Module 41 | class NetworkModule { 42 | companion object { 43 | 44 | private const val TIMEOUT_IN_MS = 30000 45 | private const val LT_BASE_URL = "lt_base_url" 46 | private const val TOKEN = "token" 47 | 48 | private val BASE_URL = "https://api.github.com/" 49 | } 50 | 51 | @Provides 52 | @ApplicationScoped 53 | fun provideAuthorizationInterceptor( 54 | @Named(TOKEN) token: String, connection: CheckInternetConnection 55 | ): Interceptor { 56 | return Interceptor { chain -> 57 | 58 | 59 | var request = chain.request() 60 | 61 | val requestBuilder = request.newBuilder() 62 | // .addHeader("Authorization", "Bearer " + token); 63 | val cacheControl = CacheControl.Builder() 64 | .maxAge(40, TimeUnit.SECONDS) 65 | .build() 66 | 67 | if (connection.isNetworkAvailable()) { 68 | val maxAge = 20 // read from cache for 1 minute 69 | //request.newBuilder() 70 | requestBuilder 71 | // .addHeader("Authorization", "Bearer$token") 72 | 73 | .addHeader( 74 | "Authorization", 75 | "Bearer $token" 76 | ) 77 | .addHeader("Content-Type", "application/json") 78 | .header("Cache-Control", "public, max-age=$maxAge") 79 | .cacheControl(cacheControl) 80 | .build() 81 | } else { 82 | val maxStale = 60 * 60 * 24 * 28 // tolerate 4-weeks stale 83 | // request.newBuilder() 84 | requestBuilder 85 | .header("Cache-Control", "public, only-if-cached, max-stale=$maxStale") 86 | .build() 87 | } 88 | 89 | request = requestBuilder.build() 90 | 91 | val response = chain.proceed(request) 92 | if (response.code() == 401) { 93 | 94 | // Expire Token 95 | return@Interceptor response 96 | } 97 | response 98 | } 99 | } 100 | 101 | @Provides 102 | @Named(LT_BASE_URL) 103 | fun provideBaseUrlString(): String { 104 | return BASE_URL 105 | 106 | } 107 | 108 | 109 | @Provides 110 | @ApplicationScoped 111 | internal fun provideRetrofitInterface( 112 | okHttpClient: OkHttpClient 113 | ): Retrofit { 114 | 115 | return Retrofit.Builder() 116 | .baseUrl(BASE_URL) 117 | .addConverterFactory(MoshiConverterFactory.create()) 118 | 119 | .build() 120 | 121 | } 122 | 123 | 124 | @Provides 125 | @Named(TOKEN) 126 | fun provideToken(preference: AppPrefs): String? { 127 | return preference.TOKEN 128 | } 129 | 130 | @RequiresApi(Build.VERSION_CODES.GINGERBREAD) 131 | @Provides 132 | @ApplicationScoped 133 | fun provideCookieManager(): CookieManager? { 134 | val cookieManager = CookieManager() 135 | cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL) 136 | CookieHandler.setDefault(cookieManager) 137 | return cookieManager 138 | } 139 | 140 | @Provides 141 | @ApplicationScoped 142 | fun provideLoggingInterceptor(): HttpLoggingInterceptor? { 143 | return HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY) 144 | } 145 | 146 | @Provides 147 | @ApplicationScoped 148 | fun provideOKHttpClient( 149 | loggingInterceptor: HttpLoggingInterceptor, 150 | authInterceptor: Interceptor, 151 | cookieJar: CookieJar, 152 | cache: Cache 153 | ): OkHttpClient { 154 | 155 | // Create a trust manager that does not validate certificate chains 156 | val trustAllCerts = 157 | arrayOf( 158 | object : X509TrustManager { 159 | @Throws(CertificateException::class) 160 | override fun checkClientTrusted( 161 | chain: Array, 162 | authType: String 163 | ) { 164 | } 165 | 166 | @Throws(CertificateException::class) 167 | override fun checkServerTrusted( 168 | chain: Array, 169 | authType: String 170 | ) { 171 | } 172 | 173 | override fun getAcceptedIssuers(): Array { 174 | return arrayOf() 175 | } 176 | } 177 | ) 178 | 179 | // Install the all-trusting trust manager 180 | 181 | // Install the all-trusting trust manager 182 | val sslContext = SSLContext.getInstance("SSL") 183 | sslContext.init(null, trustAllCerts, SecureRandom()) 184 | 185 | // Create an ssl socket factory with our all-trusting manager 186 | 187 | // Create an ssl socket factory with our all-trusting manager 188 | val sslSocketFactory = sslContext.socketFactory 189 | 190 | val builder = OkHttpClient.Builder() 191 | builder.sslSocketFactory( 192 | sslSocketFactory, 193 | trustAllCerts[0] as X509TrustManager 194 | ) 195 | builder.hostnameVerifier { hostname, session -> true } 196 | return OkHttpClient.Builder() 197 | 198 | .connectTimeout(1, TimeUnit.MINUTES) 199 | .writeTimeout(1, TimeUnit.MINUTES) // write timeout 200 | .readTimeout(1, TimeUnit.MINUTES) // read timeout 201 | .addInterceptor(authInterceptor) 202 | .addInterceptor(loggingInterceptor) 203 | .cookieJar(cookieJar) 204 | .cache(cache) 205 | .build() 206 | 207 | } 208 | 209 | @Provides 210 | @ApplicationScoped 211 | fun provideMoshi(): MoshiConverterFactory { 212 | return MoshiConverterFactory.create(Moshi.Builder().add(KotlinJsonAdapterFactory()).build()) 213 | } 214 | 215 | @Provides 216 | @ApplicationScoped 217 | fun provideRxAdapter(): RxJava2CallAdapterFactory { 218 | return RxJava2CallAdapterFactory 219 | .createWithScheduler(Schedulers.io()) 220 | } 221 | 222 | @Provides 223 | @ApplicationScoped 224 | fun provideexampleService(@Named(LT_BASE_URL) baseUrl: String): SynchronousApi { 225 | return Retrofit.Builder().baseUrl(baseUrl) 226 | .addConverterFactory(GsonConverterFactory.create()) 227 | .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) 228 | .addConverterFactory(ScalarsConverterFactory.create()) 229 | .build().create(SynchronousApi::class.java) 230 | } 231 | 232 | @Provides 233 | @ApplicationScoped 234 | fun provideGson(): Gson? { 235 | return GsonBuilder() /* .registerTypeAdapterFactory(tvMazeTypeAdaptorFactory)*/ 236 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 237 | .setLenient() 238 | .create() 239 | } 240 | 241 | @Provides 242 | @ApplicationScoped 243 | fun provideCookieJar(context: Application?): CookieJar? { 244 | return PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(context)) 245 | } 246 | 247 | 248 | @Provides 249 | @ApplicationScoped 250 | fun provideCache(context: Application): Cache? { 251 | val cacheSize = 5 * 1024 * 1024 // 5 MB 252 | val cacheDir = context.cacheDir 253 | return Cache(cacheDir, cacheSize.toLong()) 254 | } 255 | 256 | @Provides 257 | @ApplicationScoped 258 | fun provideRxJavaCallAdapterFactory(): RxJava2CallAdapterFactory? { 259 | return RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()) 260 | } 261 | } 262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/module/PreferenceModule.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.module 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import com.technorapper.boiler.constants.Preferences 6 | 7 | import com.technorapper.boiler.application.App 8 | import com.technorapper.boiler.di.scopes.ApplicationScoped 9 | 10 | import com.technorapper.boiler.helpers.AppPrefs 11 | import dagger.Module 12 | import dagger.Provides 13 | 14 | @Module 15 | open class PreferenceModule { 16 | 17 | @Provides 18 | @ApplicationScoped 19 | open fun provideTechnorapperPreference(preferences: SharedPreferences?): AppPrefs { 20 | return AppPrefs(preferences) 21 | } 22 | 23 | @Provides 24 | @ApplicationScoped 25 | open fun providePreferences(context: App): SharedPreferences { 26 | return context.getSharedPreferences(Preferences.APP_NAME, Context.MODE_PRIVATE) 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/module/ViewModelKey.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.module 2 | 3 | import androidx.lifecycle.ViewModel 4 | import dagger.MapKey 5 | import kotlin.reflect.KClass 6 | 7 | 8 | @MustBeDocumented 9 | @Target(AnnotationTarget.FUNCTION) 10 | @Retention(AnnotationRetention.RUNTIME) 11 | @MapKey 12 | internal annotation class ViewModelKey(val value: KClass) -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/module/ViewModelModule.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.module 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | 6 | import com.technorapper.boiler.di.WDViewModelFactory 7 | import com.technorapper.boiler.ui.onboarding.activity.MainActivityViewModel 8 | import dagger.Binds 9 | import dagger.Module 10 | import dagger.multibindings.IntoMap 11 | 12 | @Module 13 | abstract class ViewModelModule { 14 | 15 | @Binds 16 | @IntoMap 17 | @ViewModelKey(MainActivityViewModel::class) 18 | abstract fun bindMyViewModel(mainActivityViewModel: MainActivityViewModel): ViewModel 19 | 20 | @Binds 21 | abstract fun bindViewModelFactory(viewModelFactory: WDViewModelFactory): ViewModelProvider.Factory 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/scopes/ActivityScoped.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.scopes 2 | 3 | import java.lang.annotation.Documented 4 | import java.lang.annotation.Retention 5 | import java.lang.annotation.RetentionPolicy 6 | import javax.inject.Scope 7 | 8 | @kotlin.annotation.MustBeDocumented 9 | @Scope 10 | @kotlin.annotation.Retention 11 | annotation class ActivityScoped -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/scopes/ApplicationScoped.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.scopes 2 | 3 | import java.lang.annotation.Documented 4 | import java.lang.annotation.Retention 5 | import java.lang.annotation.RetentionPolicy 6 | import javax.inject.Scope 7 | 8 | @kotlin.annotation.MustBeDocumented 9 | @Scope 10 | @kotlin.annotation.Retention 11 | annotation class ApplicationScoped -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/scopes/BindingScoped.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.scopes 2 | 3 | import java.lang.annotation.Retention 4 | import java.lang.annotation.RetentionPolicy 5 | import javax.inject.Scope 6 | 7 | @Scope 8 | @kotlin.annotation.Retention 9 | @Target( 10 | AnnotationTarget.ANNOTATION_CLASS, 11 | AnnotationTarget.CLASS, 12 | AnnotationTarget.FUNCTION, 13 | AnnotationTarget.PROPERTY_GETTER, 14 | AnnotationTarget.PROPERTY_SETTER 15 | ) 16 | annotation class BindingScoped 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/di/scopes/RandomUserApplicationScope.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.di.scopes 2 | 3 | import java.lang.annotation.RetentionPolicy 4 | import javax.inject.Scope 5 | 6 | @Scope 7 | @kotlin.annotation.Retention 8 | annotation class RandomUserApplicationScope -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/global/CustomBindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.global 2 | 3 | import android.widget.ImageView 4 | import androidx.databinding.BindingAdapter 5 | import com.technorapper.boiler.R 6 | import com.squareup.picasso.Picasso 7 | 8 | 9 | class CustomBindingAdapter(private val picasso: Picasso) { 10 | 11 | /* @BindingAdapter("android:src_url") 12 | fun loadImage(view: ImageView, url: String?) { 13 | if (url == null || url.equals("", ignoreCase = true)) { 14 | view.setImageResource(R.mipmap.ic_launcher) 15 | return 16 | } 17 | picasso.load(url).error(R.mipmap.ic_launcher).fit().into(view) 18 | }*/ 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/helpers/AppPrefs.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.helpers 2 | 3 | import android.content.SharedPreferences 4 | import javax.inject.Inject 5 | 6 | class AppPrefs @Inject constructor(private val pref: SharedPreferences?) { 7 | var IS_LOGIN = "is_login" 8 | var TOKEN_ = "user_token" 9 | 10 | var isLogin: Boolean 11 | get() = pref!!.getBoolean(IS_LOGIN, false) 12 | set(value) { 13 | pref?.edit()?.putBoolean(IS_LOGIN, value)?.apply() 14 | } 15 | 16 | var TOKEN: String? 17 | get() = pref!!.getString(TOKEN_, "") 18 | set(value) { 19 | pref?.edit()?.putString(TOKEN_, value)?.apply() 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/ui/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.ui.base 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import androidx.lifecycle.ViewModelProvider 6 | import com.technorapper.boiler.di.Injectable 7 | import dagger.android.DispatchingAndroidInjector 8 | import dagger.android.support.DaggerAppCompatActivity 9 | import dagger.android.support.HasSupportFragmentInjector 10 | import javax.inject.Inject 11 | 12 | abstract class BaseActivity : DaggerAppCompatActivity(), Injectable, HasSupportFragmentInjector{ 13 | 14 | @Inject lateinit var viewModelFactory: ViewModelProvider.Factory 15 | @Inject 16 | lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | setBinding() 21 | attachViewModel() 22 | 23 | } 24 | 25 | protected abstract fun setBinding() 26 | 27 | protected abstract fun attachViewModel() 28 | 29 | override fun supportFragmentInjector() = fragmentDispatchingAndroidInjector 30 | 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/ui/base/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.ui.base 2 | 3 | import androidx.lifecycle.ViewModel 4 | import io.reactivex.disposables.CompositeDisposable 5 | import io.reactivex.disposables.Disposable 6 | 7 | open class BaseViewModel : ViewModel() { 8 | val compositeDisposable = CompositeDisposable() 9 | 10 | protected fun addToDisposable(disposable: Disposable?) { 11 | compositeDisposable.add(disposable!!) 12 | } 13 | 14 | fun onStop() { 15 | compositeDisposable.dispose() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/technorapper/boiler/ui/onboarding/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.technorapper.boiler.ui.onboarding.activity 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.widget.Button 7 | import androidx.lifecycle.ViewModelProvider 8 | import androidx.lifecycle.ViewModelProviders 9 | import com.technorapper.boiler.R 10 | import com.technorapper.boiler.data.tunnel.database.dao.UserMasterDao 11 | import com.technorapper.boiler.data.tunnel.remote.utils.AppSocket 12 | import com.technorapper.boiler.helpers.AppPrefs 13 | import com.technorapper.boiler.ui.base.BaseActivity 14 | import com.github.nkzawa.emitter.Emitter 15 | import kotlinx.android.synthetic.main.activity_main.* 16 | import timber.log.Timber 17 | import javax.inject.Inject 18 | 19 | class MainActivity : BaseActivity() { 20 | 21 | 22 | private lateinit var viewModel: MainActivityViewModel 23 | @Inject 24 | lateinit var socket: AppSocket 25 | @Inject 26 | lateinit var context: Context 27 | @Inject 28 | lateinit var sharedPreferences: AppPrefs 29 | 30 | @Inject 31 | lateinit var userMasterDao: UserMasterDao 32 | 33 | override fun onCreate(savedInstanceState: Bundle?) { 34 | super.onCreate(savedInstanceState) 35 | setContentView(R.layout.activity_main) 36 | if (sharedPreferences.isLogin) { 37 | Log.d("dff", userMasterDao.getAll().toString()); 38 | } 39 | 40 | // val button = findViewById