├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml └── vcs.xml ├── Config ├── README.md ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── devm7mdibrahim │ │ └── base_android │ │ ├── BaseApp.kt │ │ └── fcm │ │ ├── FirebaseMessagingReceiver.kt │ │ ├── NotificationItem.kt │ │ └── NotificationKey.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ └── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp ├── build.gradle ├── buildSrc ├── build.gradle.kts ├── build │ ├── classes │ │ └── kotlin │ │ │ └── main │ │ │ ├── AndroidTestDependencies.class │ │ │ ├── ApplicationId.class │ │ │ ├── Build.class │ │ │ ├── Compilers.class │ │ │ ├── Dependencies.class │ │ │ ├── META-INF │ │ │ └── buildSrc.kotlin_module │ │ │ ├── SupportDependencies.class │ │ │ ├── TestDependencies.class │ │ │ └── Versions.class │ ├── kotlin │ │ ├── buildSrcjar-classes.txt │ │ └── compileKotlin │ │ │ ├── build-history.bin │ │ │ ├── caches-jvm │ │ │ ├── inputs │ │ │ │ ├── source-to-output.tab │ │ │ │ ├── source-to-output.tab.keystream │ │ │ │ ├── source-to-output.tab.keystream.len │ │ │ │ ├── source-to-output.tab.len │ │ │ │ ├── source-to-output.tab.values.at │ │ │ │ ├── source-to-output.tab_i │ │ │ │ └── source-to-output.tab_i.len │ │ │ ├── jvm │ │ │ │ └── kotlin │ │ │ │ │ ├── class-attributes.tab │ │ │ │ │ ├── class-attributes.tab.keystream │ │ │ │ │ ├── class-attributes.tab.keystream.len │ │ │ │ │ ├── class-attributes.tab.len │ │ │ │ │ ├── class-attributes.tab.values.at │ │ │ │ │ ├── class-attributes.tab_i │ │ │ │ │ ├── class-attributes.tab_i.len │ │ │ │ │ ├── class-fq-name-to-source.tab │ │ │ │ │ ├── class-fq-name-to-source.tab.keystream │ │ │ │ │ ├── class-fq-name-to-source.tab.keystream.len │ │ │ │ │ ├── class-fq-name-to-source.tab.len │ │ │ │ │ ├── class-fq-name-to-source.tab.values.at │ │ │ │ │ ├── class-fq-name-to-source.tab_i │ │ │ │ │ ├── class-fq-name-to-source.tab_i.len │ │ │ │ │ ├── constants.tab │ │ │ │ │ ├── constants.tab.keystream │ │ │ │ │ ├── constants.tab.keystream.len │ │ │ │ │ ├── constants.tab.len │ │ │ │ │ ├── constants.tab.values.at │ │ │ │ │ ├── constants.tab_i │ │ │ │ │ ├── constants.tab_i.len │ │ │ │ │ ├── internal-name-to-source.tab │ │ │ │ │ ├── internal-name-to-source.tab.keystream │ │ │ │ │ ├── internal-name-to-source.tab.keystream.len │ │ │ │ │ ├── internal-name-to-source.tab.len │ │ │ │ │ ├── internal-name-to-source.tab.values.at │ │ │ │ │ ├── internal-name-to-source.tab_i │ │ │ │ │ ├── internal-name-to-source.tab_i.len │ │ │ │ │ ├── proto.tab │ │ │ │ │ ├── proto.tab.keystream │ │ │ │ │ ├── proto.tab.keystream.len │ │ │ │ │ ├── proto.tab.len │ │ │ │ │ ├── proto.tab.values.at │ │ │ │ │ ├── proto.tab_i │ │ │ │ │ ├── proto.tab_i.len │ │ │ │ │ ├── source-to-classes.tab │ │ │ │ │ ├── source-to-classes.tab.keystream │ │ │ │ │ ├── source-to-classes.tab.keystream.len │ │ │ │ │ ├── source-to-classes.tab.len │ │ │ │ │ ├── source-to-classes.tab.values.at │ │ │ │ │ ├── source-to-classes.tab_i │ │ │ │ │ └── source-to-classes.tab_i.len │ │ │ └── lookups │ │ │ │ ├── counters.tab │ │ │ │ ├── file-to-id.tab │ │ │ │ ├── file-to-id.tab.keystream │ │ │ │ ├── file-to-id.tab.keystream.len │ │ │ │ ├── file-to-id.tab.len │ │ │ │ ├── file-to-id.tab.values.at │ │ │ │ ├── file-to-id.tab_i │ │ │ │ ├── file-to-id.tab_i.len │ │ │ │ ├── id-to-file.tab │ │ │ │ ├── id-to-file.tab.keystream │ │ │ │ ├── id-to-file.tab.keystream.len │ │ │ │ ├── id-to-file.tab.len │ │ │ │ ├── id-to-file.tab.values.at │ │ │ │ ├── id-to-file.tab_i │ │ │ │ ├── id-to-file.tab_i.len │ │ │ │ ├── lookups.tab │ │ │ │ ├── lookups.tab.keystream │ │ │ │ ├── lookups.tab.keystream.len │ │ │ │ ├── lookups.tab.len │ │ │ │ ├── lookups.tab.values.at │ │ │ │ ├── lookups.tab_i │ │ │ │ └── lookups.tab_i.len │ │ │ └── last-build.bin │ ├── libs │ │ └── buildSrc.jar │ ├── pluginUnderTestMetadata │ │ └── plugin-under-test-metadata.properties │ ├── reports │ │ └── plugin-development │ │ │ └── validation-report.txt │ └── tmp │ │ └── jar │ │ └── MANIFEST.MF └── src │ └── main │ └── java │ ├── AndroidTestDependencies.kt │ ├── ApplicationId.kt │ ├── Build.kt │ ├── Compilers.kt │ ├── Dependencies.kt │ ├── SupportDependencies.kt │ ├── TestDependencies.kt │ └── Versions.kt ├── data ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── devm7mdibrahim │ └── data │ ├── datasource │ ├── AuthDataSource.kt │ ├── ChatDataSource.kt │ ├── HomeDataSource.kt │ ├── PreferenceDataSource.kt │ └── SocketDataSource.kt │ ├── di │ ├── EndPointsModule.kt │ ├── NetworkModule.kt │ ├── PreferencesModule.kt │ ├── RemoteDataSourcesModule.kt │ ├── RepositoryModule.kt │ ├── SocketModule.kt │ └── StringsModule.kt │ ├── local │ ├── datasource │ │ └── PreferenceDataSourceImpl.kt │ └── utils │ │ ├── PreferenceConstants.kt │ │ └── SafeCacheCall.kt │ ├── remote │ ├── datasource │ │ ├── AuthDataSourceImpl.kt │ │ ├── ChatDataSourceImpl.kt │ │ └── HomeDataSourceImpl.kt │ ├── endpoints │ │ ├── AuthEndPoints.kt │ │ ├── ChatEndPoints.kt │ │ └── HomeEndPoints.kt │ └── utils │ │ ├── NetworkConstants.kt │ │ └── SafeApiCall.kt │ ├── repository │ ├── AuthRepositoryImpl.kt │ ├── ChatRepositoryImpl.kt │ ├── HomeRepositoryImpl.kt │ └── PreferenceRepositoryImpl.kt │ ├── socket │ └── datasource │ │ └── SocketDataSourceImpl.kt │ └── utils │ └── MultiPartUtil.kt ├── domain ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── devm7mdibrahim │ └── domain │ ├── entities │ ├── AuthData.kt │ ├── BaseResponse.kt │ ├── ChatResponse.kt │ ├── MessagesItem.kt │ ├── Pagination.kt │ ├── RoomsItem.kt │ └── RoomsResponse.kt │ ├── exceptions │ ├── LocalExceptions.kt │ ├── NetworkExceptions.kt │ └── ValidationException.kt │ ├── repository │ ├── AuthRepository.kt │ ├── ChatRepository.kt │ ├── HomeRepository.kt │ └── PreferenceRepository.kt │ ├── usecases │ ├── LoginUseCase.kt │ └── RegisterUseCase.kt │ └── util │ ├── CommonValidation.kt │ ├── Constants.kt │ └── DataState.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── presentation ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── devm7mdibrahim │ │ └── presentation │ │ ├── base │ │ ├── BaseActivity.kt │ │ ├── BaseBottomSheetFragment.kt │ │ ├── BaseDialogFragment.kt │ │ ├── BaseFragment.kt │ │ ├── BaseRecyclerAdapter.kt │ │ ├── BaseViewHolder.kt │ │ └── BaseViewModel.kt │ │ ├── cycles │ │ ├── auth_cycle │ │ │ ├── activity │ │ │ │ └── AuthActivity.kt │ │ │ └── fragment │ │ │ │ ├── login │ │ │ │ ├── LoginFragment.kt │ │ │ │ └── LoginViewModel.kt │ │ │ │ └── register │ │ │ │ ├── RegisterFragment.kt │ │ │ │ └── RegisterViewModel.kt │ │ ├── home_cycle │ │ │ ├── activity │ │ │ │ └── HomeActivity.kt │ │ │ └── fragment │ │ │ │ └── home_container │ │ │ │ ├── HomeContainerFragment.kt │ │ │ │ ├── HomeContainerViewModel.kt │ │ │ │ ├── home │ │ │ │ ├── HomeFragment.kt │ │ │ │ └── HomeViewModel.kt │ │ │ │ ├── notifications │ │ │ │ ├── NotificationsFragment.kt │ │ │ │ └── NotificationsViewModel.kt │ │ │ │ └── profile │ │ │ │ ├── ProfileFragment.kt │ │ │ │ └── ProfileViewModel.kt │ │ └── splash_cycle │ │ │ ├── activity │ │ │ └── SplashActivity.kt │ │ │ └── fragment │ │ │ └── SplashFragment.kt │ │ └── utils │ │ ├── CommonErrorHandling.kt │ │ └── Constants.kt │ └── res │ ├── anim │ ├── fade_in.xml │ ├── fade_out.xml │ ├── slide_in_left.xml │ ├── slide_in_right.xml │ ├── slide_out_left.xml │ └── slide_out_right.xml │ ├── color │ └── bottom_nav_color.xml │ ├── layout │ ├── activity_auth.xml │ ├── activity_home.xml │ ├── activity_splash.xml │ ├── base_toolbar.xml │ ├── fragment_home.xml │ ├── fragment_home_container.xml │ ├── fragment_login.xml │ ├── fragment_notifications.xml │ ├── fragment_profile.xml │ ├── fragment_register.xml │ └── fragment_splash.xml │ ├── menu │ └── home_menu.xml │ ├── navigation │ ├── auth_graph.xml │ ├── home_container_graph.xml │ ├── home_graph.xml │ └── splash_graph.xml │ ├── values-ar │ └── strings.xml │ └── values │ ├── colors.xml │ ├── google_maps_api.xml │ ├── strings.xml │ ├── styles.xml │ └── themes.xml ├── settings.gradle └── utils ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── devm7mdibrahim │ └── utils │ ├── common │ ├── NetworkHelper.kt │ ├── PaginationHelper.kt │ └── ProgressUtil.kt │ ├── di │ ├── ActivityModule.kt │ └── GlideModule.kt │ ├── extensions │ ├── AlertExtensions.kt │ ├── CommonExtensions.kt │ ├── ExceptionsExtensions.kt │ ├── FirebaseExtensions.kt │ ├── FragmentExtensions.kt │ ├── IntentExtensions.kt │ ├── JsonExtensions.kt │ ├── KeyboardExtensions.kt │ ├── NavigationExtension.kt │ ├── ThemeExtentions.kt │ ├── ToastExtenstions.kt │ └── ViewExtensions.kt │ ├── file │ └── FileUtils.kt │ └── spinner │ ├── MaterialSpinnerUtil.kt │ └── SpinnerUtil.kt └── res ├── drawable ├── bg_top_rounded_white.xml └── ic_back.xml ├── layout └── progress.xml ├── values-ar └── strings.xml └── values ├── colors.xml ├── strings.xml └── styles.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | base-android -------------------------------------------------------------------------------- /.idea/compiler.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 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Config: -------------------------------------------------------------------------------- 1 | BaseUrl = https://baseurl.com 2 | SocketPort = 4000 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # base-android 2 | 3 | - This is the base code that I prefer to work in small and medium projects. 4 | 5 | - using clean architecture and MVVM architecture pattern 6 | - coding in kotlin language 7 | 8 | # Modules 9 | - Domain: contains business logic 10 | - Data: contains data sources 11 | - presentation: contains ui 12 | - utils: contains common utils functions that are useful for fast coding 13 | 14 | # Technologies 15 | - Dagger-Hilt for dependency injection 16 | - Retrofit and Okhttp for network 17 | - Socket-io for realtime 18 | - Datastore for saving small data in (key-value) 19 | - ViewBinding for binding views 20 | - Navigation-Component for navigation between screens 21 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-kapt' 5 | id 'dagger.hilt.android.plugin' 6 | id 'com.google.gms.google-services' 7 | id 'com.google.firebase.crashlytics' 8 | id 'androidx.navigation.safeargs.kotlin' 9 | } 10 | 11 | android { 12 | compileSdk 31 13 | 14 | defaultConfig { 15 | applicationId ApplicationId.id 16 | minSdk 21 17 | targetSdk 31 18 | versionCode 1 19 | versionName "1.0" 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | kotlinOptions { 35 | jvmTarget = '1.8' 36 | } 37 | 38 | buildFeatures { 39 | viewBinding true 40 | } 41 | } 42 | 43 | dependencies { 44 | api project(':presentation') 45 | api project(':data') 46 | api project(':domain') 47 | api project(':utils') 48 | 49 | implementation Compilers.hilt_android 50 | kapt Compilers.hilt_android_compiler 51 | kapt Compilers.hilt_android_lifecycle_compiler 52 | 53 | implementation Dependencies.firebase_analytics 54 | implementation Dependencies.firebase_crashlytics 55 | implementation Dependencies.firebase_messaging 56 | implementation Dependencies.firebase_core 57 | implementation Dependencies.firebase_iid 58 | } -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "35622279526", 4 | "project_id": "base-android-27992", 5 | "storage_bucket": "base-android-27992.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:35622279526:android:b4def4dc1449b3d1f3d0a7", 11 | "android_client_info": { 12 | "package_name": "com.devm7mdibrahim.baseandroid" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "35622279526-kepnaj5e8hvhqmlhk3i20ui539ib46r6.apps.googleusercontent.com", 18 | "client_type": 3 19 | } 20 | ], 21 | "api_key": [ 22 | { 23 | "current_key": "AIzaSyCQC_iW96x0V2jC3LdwiBqfVWrFobvjDqM" 24 | } 25 | ], 26 | "services": { 27 | "appinvite_service": { 28 | "other_platform_oauth_client": [ 29 | { 30 | "client_id": "35622279526-kepnaj5e8hvhqmlhk3i20ui539ib46r6.apps.googleusercontent.com", 31 | "client_type": 3 32 | } 33 | ] 34 | } 35 | } 36 | } 37 | ], 38 | "configuration_version": "1" 39 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 39 | 42 | 43 | 48 | 49 | 50 | 51 | 52 | 53 | 56 | 57 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/devm7mdibrahim/base_android/BaseApp.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.base_android 2 | 3 | import android.app.NotificationChannel 4 | import android.app.NotificationManager 5 | import android.content.Context 6 | import android.media.AudioAttributes 7 | import android.media.RingtoneManager 8 | import android.os.Build 9 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.Languages.ARABIC 10 | import com.devm7mdibrahim.utils.extensions.ThemeHelper 11 | import com.akexorcist.localizationactivity.ui.LocalizationApplication 12 | import dagger.hilt.android.HiltAndroidApp 13 | import java.util.* 14 | 15 | @HiltAndroidApp 16 | class BaseApp : LocalizationApplication() { 17 | 18 | private lateinit var notificationManager: NotificationManager 19 | 20 | companion object { 21 | const val CHANNEL_ID = "base_channel_id" 22 | const val CHANNEL_NAME = "base_channel" 23 | } 24 | 25 | override fun getDefaultLanguage(base: Context): Locale { 26 | return Locale(ARABIC) 27 | } 28 | 29 | override fun onCreate() { 30 | super.onCreate() 31 | ThemeHelper.applyTheme(ThemeHelper.ThemeMode.LIGHT) 32 | } 33 | 34 | override fun attachBaseContext(base: Context) { 35 | super.attachBaseContext(base) 36 | notificationManager = 37 | base.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 38 | 39 | createAppChannel() 40 | } 41 | 42 | private fun createAppChannel() { 43 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 44 | 45 | val notificationChannel = NotificationChannel( 46 | CHANNEL_ID, 47 | CHANNEL_NAME, 48 | NotificationManager.IMPORTANCE_HIGH 49 | ) 50 | 51 | val defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) 52 | 53 | notificationChannel.setSound( 54 | defaultSound, 55 | AudioAttributes.Builder() 56 | .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 57 | .setUsage(AudioAttributes.USAGE_MEDIA) 58 | .build() 59 | ) 60 | 61 | notificationManager.createNotificationChannel(notificationChannel) 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devm7mdibrahim/base_android/fcm/NotificationItem.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.base_android.fcm 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class NotificationItem( 6 | @SerializedName("message_ar") val messageAr: String? = "", 7 | @SerializedName("message_en") val messageEn: String? = "", 8 | @SerializedName("title_ar") val titleAr: String? = "", 9 | @SerializedName("title_en") val titleEn: String? = "", 10 | @SerializedName("type") val type: String? = "", 11 | ) 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/devm7mdibrahim/base_android/fcm/NotificationKey.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.base_android.fcm 2 | 3 | object NotificationKey { 4 | const val NEW_ORDER = "new_order" 5 | const val NEW_MESSAGE = "new_message" 6 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | gradlePluginPortal() 5 | mavenCentral() 6 | maven { url 'https://jitpack.io' } 7 | } 8 | 9 | dependencies { 10 | classpath Build.build_tools 11 | classpath Build.kotlin_gradle_plugin 12 | classpath Build.google_services 13 | classpath Build.crashlytics_gradle 14 | classpath Build.hilt_gradle 15 | classpath Build.navigation 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | mavenCentral() 23 | maven { url "https://maven.google.com" } 24 | maven { url 'https://jitpack.io' } 25 | } 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.kotlin.dsl.`kotlin-dsl` 2 | 3 | plugins { 4 | `kotlin-dsl` 5 | } 6 | repositories { 7 | mavenCentral() 8 | } -------------------------------------------------------------------------------- /buildSrc/build/classes/kotlin/main/AndroidTestDependencies.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/classes/kotlin/main/AndroidTestDependencies.class -------------------------------------------------------------------------------- /buildSrc/build/classes/kotlin/main/ApplicationId.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/classes/kotlin/main/ApplicationId.class -------------------------------------------------------------------------------- /buildSrc/build/classes/kotlin/main/Build.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/classes/kotlin/main/Build.class -------------------------------------------------------------------------------- /buildSrc/build/classes/kotlin/main/Compilers.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/classes/kotlin/main/Compilers.class -------------------------------------------------------------------------------- /buildSrc/build/classes/kotlin/main/Dependencies.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/classes/kotlin/main/Dependencies.class -------------------------------------------------------------------------------- /buildSrc/build/classes/kotlin/main/META-INF/buildSrc.kotlin_module: -------------------------------------------------------------------------------- 1 | "* -------------------------------------------------------------------------------- /buildSrc/build/classes/kotlin/main/SupportDependencies.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/classes/kotlin/main/SupportDependencies.class -------------------------------------------------------------------------------- /buildSrc/build/classes/kotlin/main/TestDependencies.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/classes/kotlin/main/TestDependencies.class -------------------------------------------------------------------------------- /buildSrc/build/classes/kotlin/main/Versions.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/classes/kotlin/main/Versions.class -------------------------------------------------------------------------------- /buildSrc/build/kotlin/buildSrcjar-classes.txt: -------------------------------------------------------------------------------- 1 | /Users/aait/StudioProjects/base-android/buildSrc/build/classes/kotlin/main/AndroidTestDependencies.class:/Users/aait/StudioProjects/base-android/buildSrc/build/classes/kotlin/main/ApplicationId.class:/Users/aait/StudioProjects/base-android/buildSrc/build/classes/kotlin/main/Build.class:/Users/aait/StudioProjects/base-android/buildSrc/build/classes/kotlin/main/Compilers.class:/Users/aait/StudioProjects/base-android/buildSrc/build/classes/kotlin/main/Dependencies.class:/Users/aait/StudioProjects/base-android/buildSrc/build/classes/kotlin/main/SupportDependencies.class:/Users/aait/StudioProjects/base-android/buildSrc/build/classes/kotlin/main/TestDependencies.class:/Users/aait/StudioProjects/base-android/buildSrc/build/classes/kotlin/main/Versions.class -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/build-history.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/build-history.bin -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.keystream.len: -------------------------------------------------------------------------------- 1 | i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.values.at: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.values.at -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len: -------------------------------------------------------------------------------- 1 | q -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.values.at: -------------------------------------------------------------------------------- 1 | /Header Record For PersistentHashMapValueStorage -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len: -------------------------------------------------------------------------------- 1 | q -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at: -------------------------------------------------------------------------------- 1 | /Header Record For PersistentHashMapValueStorage76$PROJECT_DIR$/src/main/java/AndroidTestDependencies.kt-,$PROJECT_DIR$/src/main/java/ApplicationId.kt%$$PROJECT_DIR$/src/main/java/Build.kt)($PROJECT_DIR$/src/main/java/Compilers.kt,+$PROJECT_DIR$/src/main/java/Dependencies.kt32$PROJECT_DIR$/src/main/java/SupportDependencies.kt0/$PROJECT_DIR$/src/main/java/TestDependencies.kt('$PROJECT_DIR$/src/main/java/Versions.kt,+$PROJECT_DIR$/src/main/java/Dependencies.kt('$PROJECT_DIR$/src/main/java/Versions.kt -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.keystream.len: -------------------------------------------------------------------------------- 1 | q -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.values.at: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.values.at -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len: -------------------------------------------------------------------------------- 1 | q -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at: -------------------------------------------------------------------------------- 1 | /Header Record For PersistentHashMapValueStorage76$PROJECT_DIR$/src/main/java/AndroidTestDependencies.kt-,$PROJECT_DIR$/src/main/java/ApplicationId.kt%$$PROJECT_DIR$/src/main/java/Build.kt)($PROJECT_DIR$/src/main/java/Compilers.kt,+$PROJECT_DIR$/src/main/java/Dependencies.kt32$PROJECT_DIR$/src/main/java/SupportDependencies.kt0/$PROJECT_DIR$/src/main/java/TestDependencies.kt('$PROJECT_DIR$/src/main/java/Versions.kt,+$PROJECT_DIR$/src/main/java/Dependencies.kt('$PROJECT_DIR$/src/main/java/Versions.kt -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream: -------------------------------------------------------------------------------- 1 | AndroidTestDependencies ApplicationIdBuild Compilers DependenciesSupportDependenciesTestDependenciesVersions.kotlin_module -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.len: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.values.at: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.values.at -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len: -------------------------------------------------------------------------------- 1 | i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at: -------------------------------------------------------------------------------- 1 | /Header Record For PersistentHashMapValueStorage'AndroidTestDependencies.kotlin_module ApplicationId.kotlin_moduleBuild.kotlin_module Compilers.kotlin_module Dependencies.kotlin_module#SupportDependencies.kotlin_module TestDependencies.kotlin_moduleVersions.kotlin_module Dependencies.kotlin_moduleVersions.kotlin_module -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/counters.tab: -------------------------------------------------------------------------------- 1 | 10 2 | 0 -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.keystream.len: -------------------------------------------------------------------------------- 1 | i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.values.at: -------------------------------------------------------------------------------- 1 | /Header Record For PersistentHashMapValueStorage -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.keystream.len: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.len: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.values.at: -------------------------------------------------------------------------------- 1 | /Header Record For PersistentHashMapValueStorage76$PROJECT_DIR$/src/main/java/AndroidTestDependencies.kt-,$PROJECT_DIR$/src/main/java/ApplicationId.kt%$$PROJECT_DIR$/src/main/java/Build.kt)($PROJECT_DIR$/src/main/java/Compilers.kt,+$PROJECT_DIR$/src/main/java/Dependencies.kt32$PROJECT_DIR$/src/main/java/SupportDependencies.kt0/$PROJECT_DIR$/src/main/java/TestDependencies.kt('$PROJECT_DIR$/src/main/java/Versions.kt,+$PROJECT_DIR$/src/main/java/Dependencies.kt('$PROJECT_DIR$/src/main/java/Versions.kt -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.len: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.values.at: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.values.at -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i.len: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i.len -------------------------------------------------------------------------------- /buildSrc/build/kotlin/compileKotlin/last-build.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/kotlin/compileKotlin/last-build.bin -------------------------------------------------------------------------------- /buildSrc/build/libs/buildSrc.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/libs/buildSrc.jar -------------------------------------------------------------------------------- /buildSrc/build/pluginUnderTestMetadata/plugin-under-test-metadata.properties: -------------------------------------------------------------------------------- 1 | implementation-classpath=/Users/aait/StudioProjects/base-android/buildSrc/build/classes/java/main\:/Users/aait/StudioProjects/base-android/buildSrc/build/classes/groovy/main\:/Users/aait/StudioProjects/base-android/buildSrc/build/classes/kotlin/main\:/Users/aait/StudioProjects/base-android/buildSrc/build/resources/main 2 | -------------------------------------------------------------------------------- /buildSrc/build/reports/plugin-development/validation-report.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/buildSrc/build/reports/plugin-development/validation-report.txt -------------------------------------------------------------------------------- /buildSrc/build/tmp/jar/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | 3 | -------------------------------------------------------------------------------- /buildSrc/src/main/java/AndroidTestDependencies.kt: -------------------------------------------------------------------------------- 1 | object AndroidTestDependencies{ 2 | const val kotlin_test = "org.jetbrains.kotlin:kotlin-test-junit:${Versions.kotlin}" 3 | const val coroutines_test = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.coroutines_version}" 4 | const val espresso_core = "androidx.test.espresso:espresso-core:${Versions.espresso_core}" 5 | const val espresso_contrib = "androidx.test.espresso:espresso-contrib:${Versions.espresso_core}" 6 | const val idling_resource = "androidx.test.espresso:espresso-idling-resource:${Versions.espresso_idling_resource}" 7 | const val test_runner = "androidx.test:runner:${Versions.test_runner}" 8 | const val test_rules = "androidx.test:rules:${Versions.test_runner}" 9 | const val text_core_ktx = "androidx.test:core-ktx:${Versions.test_core}" 10 | const val mockk_android = "io.mockk:mockk-android:${Versions.mockk_version}" 11 | const val fragment_testing = "androidx.fragment:fragment-testing:${Versions.fragment_version}" 12 | const val androidx_test_ext = "androidx.test.ext:junit-ktx:${Versions.androidx_test_ext}" 13 | const val navigation_testing = "androidx.navigation:navigation-testing:${Versions.nav_components}" 14 | 15 | const val instrumentation_runner = "com.codingwithmitch.cleannotes.framework.MockTestRunner" 16 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/ApplicationId.kt: -------------------------------------------------------------------------------- 1 | object ApplicationId { 2 | const val id = "com.devm7mdibrahim.baseandroid" 3 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/Build.kt: -------------------------------------------------------------------------------- 1 | object Build { 2 | const val build_tools = "com.android.tools.build:gradle:7.2.1" 3 | const val kotlin_gradle_plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.0" 4 | const val google_services = "com.google.gms:google-services:${Versions.play_services}" 5 | const val junit5 = "de.mannodermaus.gradle.plugins:android-junit5:1.3.2.0" 6 | const val navigation = "androidx.navigation:navigation-safe-args-gradle-plugin:${Versions.nav_version}" 7 | const val crashlytics_gradle = "com.google.firebase:firebase-crashlytics-gradle:${Versions.crashlytics_gradle}" 8 | const val hilt_gradle = "com.google.dagger:hilt-android-gradle-plugin:2.38.1" 9 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/Compilers.kt: -------------------------------------------------------------------------------- 1 | object Compilers { 2 | const val room_compiler = "androidx.room:room-compiler:${Versions.room}" 3 | const val lifecycle_compiler = "androidx.lifecycle:lifecycle-compiler:${Versions.lifecycle_version}" 4 | 5 | const val dagger = "com.google.dagger:dagger:${Versions.hiltVersion}" 6 | const val hilt_android = "com.google.dagger:hilt-android:${Versions.hiltVersion}" 7 | const val hilt_android_compiler = "com.google.dagger:hilt-android-compiler:${Versions.hiltVersion}" 8 | const val hilt_android_lifecycle_compiler = 9 | "androidx.hilt:hilt-compiler:${Versions.hiltAndroidXVersionCompiler}" 10 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/SupportDependencies.kt: -------------------------------------------------------------------------------- 1 | object SupportDependencies { 2 | const val support_lib = "androidx.legacy:legacy-support-v4:1.0.0" 3 | const val appcompat = "androidx.appcompat:appcompat:${Versions.appcompat}" 4 | const val constraintlayout = "androidx.constraintlayout:constraintlayout:${Versions.constraintlayout}" 5 | const val material_design = "com.google.android.material:material:${Versions.material_design}" 6 | const val swipe_refresh_layout = "androidx.swiperefreshlayout:swiperefreshlayout:${Versions.swipe_refresh_layout}" 7 | const val circular_image_view = "de.hdodenhof:circleimageview:${Versions.circular_image_view}" 8 | 9 | const val sdp_library = "com.intuit.sdp:sdp-android:1.0.6" 10 | const val ssp_library = "com.intuit.ssp:ssp-android:1.0.6" 11 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/TestDependencies.kt: -------------------------------------------------------------------------------- 1 | object TestDependencies { 2 | const val jupiter_api = "org.junit.jupiter:junit-jupiter-api:${Versions.junit_jupiter_version}" 3 | const val jupiter_params = "org.junit.jupiter:junit-jupiter-params:${Versions.junit_jupiter_version}" 4 | const val jupiter_engine = "org.junit.jupiter:junit-jupiter-engine:${Versions.junit_jupiter_version}" 5 | const val mockk = "io.mockk:mockk:${Versions.mockk_version}" 6 | const val junit4 = "junit:junit:${Versions.junit_4_version}" 7 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/Versions.kt: -------------------------------------------------------------------------------- 1 | object Versions { 2 | const val gradle = "7.2.1" 3 | const val compilesdk = 30 4 | const val buildTool = 30 5 | const val buildToolsVersion = "30.0.3" 6 | const val minsdk = 21 7 | const val targetsdk = 30 8 | const val version_code = 1 9 | const val version_name = "1.0" 10 | const val kotlin = "1.5.0" 11 | const val java = "1.8" 12 | const val ktx = "1.2.0" 13 | const val multidex = "2.0.1" 14 | const val dagger = "2.25.4" 15 | const val nav_components = "2.4.1" 16 | const val material_dialogs = "3.2.1" 17 | const val room = "2.3.0" 18 | const val datastore = "1.0.0-beta01" 19 | const val appcompat = "1.1.0-rc01" 20 | const val constraintlayout = "1.1.3" 21 | const val material_design = "1.1.0" 22 | const val play_core = "1.7.1" 23 | const val play_services = "4.3.4" 24 | const val nav_version = "2.4.1" 25 | const val leak_canary = "2.0-alpha-3" 26 | const val swipe_refresh_layout = "1.1.0-alpha03" 27 | const val circular_image_view = "3.1.0" 28 | 29 | const val firebase_analytics = "17.4.1" 30 | const val firebase_crashlytics = "17.0.0" 31 | const val firebase_messaging = "22.0.0" 32 | const val firebase_auth = "19.3.0" 33 | const val firebase_core = "19.0.2" 34 | 35 | const val espresso_core = "3.1.1" 36 | const val espresso_idling_resource = "3.2.0" 37 | const val mockk_version = "1.9.2" 38 | const val test_runner = "1.2.0" 39 | const val test_core = "1.2.0" 40 | const val coroutines_version = "1.3.0" 41 | const val coroutines_play_services = "1.3.2" 42 | const val lifecycle_version = "2.2.0-alpha03" 43 | const val retrofit2_version = "2.9.0" 44 | const val moshi = "1.9.3" 45 | const val markdown_processor = "0.1.3" 46 | const val junit_jupiter_version = "5.6.0" 47 | const val junit_4_version = "4.12" 48 | const val fragment_version = "1.2.0" 49 | const val androidx_test_ext = "1.1.1" 50 | const val crashlytics_gradle = "2.7.1" 51 | const val hiltVersion = "2.38.1" 52 | const val hiltAndroidXVersionCompiler = "1.0.0" 53 | const val logging_interceptor = "3.0.0" 54 | const val rxjava_version = "3.0.0" 55 | const val rxjavaAndroid_version = "3.0.11" 56 | const val reactivestreams_version = "1.1.1" 57 | const val rxjava2adapter_version = "2.5.0" 58 | const val version_kotlin_coroutines = "1.3.7" 59 | const val version_retrofit_coroutines_adapter = "0.9.2" 60 | const val circle_image = "3.1.0" 61 | 62 | const val javaxAnnotationVersion = "1.0" 63 | const val javaxInjectVersion = "1" 64 | } -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /data/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | 8 | def myConfigPropertiesFile = rootProject.file("config") 9 | def myConfigProperties = new Properties() 10 | myConfigProperties.load(new FileInputStream(myConfigPropertiesFile)) 11 | 12 | android { 13 | compileSdk 31 14 | 15 | defaultConfig { 16 | minSdk 21 17 | targetSdk 31 18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 19 | consumerProguardFiles "consumer-rules.pro" 20 | } 21 | 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_8 24 | targetCompatibility JavaVersion.VERSION_1_8 25 | } 26 | 27 | kotlinOptions { 28 | jvmTarget = '1.8' 29 | } 30 | 31 | buildTypes { 32 | debug { 33 | buildConfigField("String", "BASE_URL","\"${myConfigProperties['BaseUrl']}\"") 34 | buildConfigField("String", "SOCKET_PORT","\"${myConfigProperties['SocketPort']}\"") 35 | } 36 | release { 37 | buildConfigField("String", "BASE_URL","\"${myConfigProperties['BaseUrl']}\"") 38 | buildConfigField("String", "SOCKET_PORT","\"${myConfigProperties['SocketPort']}\"") 39 | } 40 | } 41 | kapt { 42 | javacOptions { 43 | // These options are normally set automatically via the Hilt Gradle plugin, but we 44 | // set them manually to workaround a bug in the Kotlin 1.5.20 45 | option("-Adagger.fastInit=ENABLED") 46 | option("-Adagger.hilt.android.internal.disableAndroidSuperclassValidation=true") 47 | } 48 | } 49 | } 50 | 51 | 52 | dependencies { 53 | api project(':domain') 54 | 55 | api Dependencies.datastore 56 | api Dependencies.datastore_preference 57 | 58 | api Dependencies.retrofit 59 | api Dependencies.logging_interceptor 60 | api Dependencies.moshi_converter 61 | api Dependencies.moshi_kotlin 62 | api Dependencies.gson 63 | 64 | api Dependencies.room_runtime 65 | api Dependencies.room_ktx 66 | kapt Compilers.room_compiler 67 | 68 | api Dependencies.socket_io 69 | 70 | implementation Dependencies.coroutine 71 | implementation Dependencies.coroutine_android 72 | implementation Dependencies.coroutine_adapter 73 | 74 | implementation Compilers.hilt_android 75 | kapt Compilers.hilt_android_compiler 76 | kapt Compilers.hilt_android_lifecycle_compiler 77 | } -------------------------------------------------------------------------------- /data/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/data/consumer-rules.pro -------------------------------------------------------------------------------- /data/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 -------------------------------------------------------------------------------- /data/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/datasource/AuthDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.datasource 2 | 3 | import com.devm7mdibrahim.domain.entities.AuthData 4 | import com.devm7mdibrahim.domain.entities.BaseResponse 5 | 6 | interface AuthDataSource { 7 | 8 | suspend fun login( 9 | phone: String, 10 | password: String, 11 | deviceId: String 12 | ): BaseResponse 13 | 14 | suspend fun register( 15 | name: String, 16 | phone: String, 17 | email: String, 18 | password: String, 19 | avatar: String?, 20 | ): BaseResponse 21 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/datasource/ChatDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.datasource 2 | 3 | import com.devm7mdibrahim.domain.entities.* 4 | 5 | interface ChatDataSource { 6 | suspend fun getRooms(): BaseResponse 7 | suspend fun getChatMessages(roomId: Int): BaseResponse 8 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/datasource/HomeDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.datasource 2 | 3 | interface HomeDataSource { 4 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/datasource/PreferenceDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.datasource 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | 5 | interface PreferenceDataSource { 6 | suspend fun getValue(key: String, default: Any?): Flow 7 | suspend fun setValue(key: String, value: Any?) 8 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/datasource/SocketDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.datasource 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | import org.json.JSONObject 5 | 6 | interface SocketDataSource { 7 | fun connectSocket(): Flow 8 | fun disconnectSocket() 9 | fun openChannel(channel: String): Flow> 10 | fun setEmit(emitType: String, jsonObject: JSONObject?) 11 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/di/EndPointsModule.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.di 2 | 3 | import com.devm7mdibrahim.data.remote.endpoints.AuthEndPoints 4 | import com.devm7mdibrahim.data.remote.endpoints.ChatEndPoints 5 | import com.devm7mdibrahim.data.remote.endpoints.HomeEndPoints 6 | import dagger.Module 7 | import dagger.Provides 8 | import dagger.hilt.InstallIn 9 | import dagger.hilt.components.SingletonComponent 10 | import retrofit2.Retrofit 11 | import javax.inject.Singleton 12 | 13 | @Module 14 | @InstallIn(SingletonComponent::class) 15 | object EndPointsModule { 16 | 17 | @Provides 18 | @Singleton 19 | fun provideAuthEndPoints(retrofit: Retrofit): AuthEndPoints = 20 | retrofit.create(AuthEndPoints::class.java) 21 | 22 | @Provides 23 | @Singleton 24 | fun provideHomeEndPoints(retrofit: Retrofit): HomeEndPoints = 25 | retrofit.create(HomeEndPoints::class.java) 26 | 27 | @Provides 28 | @Singleton 29 | fun provideChatEndPoints(retrofit: Retrofit): ChatEndPoints = 30 | retrofit.create(ChatEndPoints::class.java) 31 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/di/PreferencesModule.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.di 2 | 3 | import android.content.Context 4 | import androidx.datastore.core.DataStore 5 | import androidx.datastore.preferences.core.PreferenceDataStoreFactory 6 | import androidx.datastore.preferences.core.Preferences 7 | import androidx.datastore.preferences.preferencesDataStoreFile 8 | import com.devm7mdibrahim.data.datasource.PreferenceDataSource 9 | import com.devm7mdibrahim.data.local.datasource.PreferenceDataSourceImpl 10 | import com.devm7mdibrahim.data.repository.PreferenceRepositoryImpl 11 | import com.devm7mdibrahim.domain.repository.PreferenceRepository 12 | import dagger.Module 13 | import dagger.Provides 14 | import dagger.hilt.InstallIn 15 | import dagger.hilt.android.qualifiers.ApplicationContext 16 | import dagger.hilt.components.SingletonComponent 17 | import javax.inject.Singleton 18 | 19 | @Module 20 | @InstallIn(SingletonComponent::class) 21 | object PreferencesModule { 22 | 23 | private const val PREFERENCE_NAME = "app_preferences" 24 | 25 | @Provides 26 | @Singleton 27 | fun providePreferencesDataStore(@ApplicationContext appContext: Context): DataStore = 28 | PreferenceDataStoreFactory.create( 29 | produceFile = { 30 | appContext.preferencesDataStoreFile(PREFERENCE_NAME) 31 | } 32 | ) 33 | 34 | @Provides 35 | @Singleton 36 | fun providePreferencesDataSource(dataStore: DataStore): PreferenceDataSource = 37 | PreferenceDataSourceImpl(dataStore) 38 | 39 | @Provides 40 | @Singleton 41 | fun providePreferencesRepository(preferencesDataSource: PreferenceDataSource): PreferenceRepository = 42 | PreferenceRepositoryImpl(preferencesDataSource) 43 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/di/RemoteDataSourcesModule.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.di 2 | 3 | import com.devm7mdibrahim.data.datasource.AuthDataSource 4 | import com.devm7mdibrahim.data.datasource.ChatDataSource 5 | import com.devm7mdibrahim.data.datasource.HomeDataSource 6 | import com.devm7mdibrahim.data.remote.datasource.AuthDataSourceImpl 7 | import com.devm7mdibrahim.data.remote.datasource.ChatDataSourceImpl 8 | import com.devm7mdibrahim.data.remote.datasource.HomeDataSourceImpl 9 | import com.devm7mdibrahim.data.remote.endpoints.AuthEndPoints 10 | import com.devm7mdibrahim.data.remote.endpoints.ChatEndPoints 11 | import com.devm7mdibrahim.data.remote.endpoints.HomeEndPoints 12 | import dagger.Module 13 | import dagger.Provides 14 | import dagger.hilt.InstallIn 15 | import dagger.hilt.components.SingletonComponent 16 | import javax.inject.Singleton 17 | 18 | @Module 19 | @InstallIn(SingletonComponent::class) 20 | object RemoteDataSourcesModule { 21 | 22 | @Provides 23 | @Singleton 24 | fun provideAuthRemoteDataSource(authEndPoints: AuthEndPoints): AuthDataSource = 25 | AuthDataSourceImpl(authEndPoints) 26 | 27 | @Provides 28 | @Singleton 29 | fun provideHomeRemoteDataSource(homeEndPoints: HomeEndPoints): HomeDataSource = 30 | HomeDataSourceImpl(homeEndPoints) 31 | 32 | @Provides 33 | @Singleton 34 | fun provideChatRemoteDataSource(chatEndPoints: ChatEndPoints): ChatDataSource = 35 | ChatDataSourceImpl(chatEndPoints) 36 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/di/RepositoryModule.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.di 2 | 3 | import com.devm7mdibrahim.data.datasource.AuthDataSource 4 | import com.devm7mdibrahim.data.datasource.ChatDataSource 5 | import com.devm7mdibrahim.data.datasource.HomeDataSource 6 | import com.devm7mdibrahim.data.datasource.SocketDataSource 7 | import com.devm7mdibrahim.data.repository.AuthRepositoryImpl 8 | import com.devm7mdibrahim.data.repository.ChatRepositoryImpl 9 | import com.devm7mdibrahim.data.repository.HomeRepositoryImpl 10 | import com.devm7mdibrahim.domain.repository.AuthRepository 11 | import com.devm7mdibrahim.domain.repository.ChatRepository 12 | import com.devm7mdibrahim.domain.repository.HomeRepository 13 | import dagger.Module 14 | import dagger.Provides 15 | import dagger.hilt.InstallIn 16 | import dagger.hilt.components.SingletonComponent 17 | import javax.inject.Singleton 18 | 19 | @Module 20 | @InstallIn(SingletonComponent::class) 21 | object RepositoryModule { 22 | 23 | @Provides 24 | @Singleton 25 | fun provideAuthRepository(authDataSource: AuthDataSource): AuthRepository = 26 | AuthRepositoryImpl(authDataSource) 27 | 28 | @Provides 29 | @Singleton 30 | fun provideHomeRepository(homeDataSource: HomeDataSource): HomeRepository = 31 | HomeRepositoryImpl(homeDataSource) 32 | 33 | @Provides 34 | @Singleton 35 | fun provideChatRepository(chatDataSource: ChatDataSource, socketDataSource: SocketDataSource): ChatRepository = 36 | ChatRepositoryImpl(chatDataSource, socketDataSource) 37 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/di/SocketModule.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.di 2 | 3 | import com.devm7mdibrahim.data.datasource.SocketDataSource 4 | import com.devm7mdibrahim.data.socket.datasource.SocketDataSourceImpl 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.components.SingletonComponent 9 | import io.socket.client.IO 10 | import io.socket.client.Socket 11 | import javax.inject.Singleton 12 | 13 | @Module 14 | @InstallIn(SingletonComponent::class) 15 | object SocketModule { 16 | 17 | @Provides 18 | @Singleton 19 | fun provideSocketDataSource(socket: Socket): SocketDataSource { 20 | return SocketDataSourceImpl(socket) 21 | } 22 | 23 | @Provides 24 | @Singleton 25 | fun provideSocket(@StringsModule.SocketBaseUrl socketBaseUrl: String): Socket { 26 | return IO.socket(socketBaseUrl) 27 | } 28 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/di/StringsModule.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.di 2 | 3 | import com.devm7mdibrahim.data.BuildConfig 4 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.API_VERSION 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.components.SingletonComponent 9 | import javax.inject.Qualifier 10 | 11 | @Module 12 | @InstallIn(SingletonComponent::class) 13 | object StringsModule { 14 | 15 | @Qualifier 16 | @Retention(AnnotationRetention.BINARY) 17 | annotation class BaseUrl 18 | 19 | @Qualifier 20 | @Retention(AnnotationRetention.BINARY) 21 | annotation class RemoteBaseUrl 22 | 23 | @Qualifier 24 | @Retention(AnnotationRetention.BINARY) 25 | annotation class SocketPort 26 | 27 | @Qualifier 28 | @Retention(AnnotationRetention.BINARY) 29 | annotation class SocketBaseUrl 30 | 31 | @Provides 32 | @BaseUrl 33 | fun provideBaseUrl(): String = BuildConfig.BASE_URL 34 | 35 | @Provides 36 | @RemoteBaseUrl 37 | fun provideRemoteBaseUrl(@BaseUrl baseUrl: String): String = "$baseUrl${API_VERSION}" 38 | 39 | @Provides 40 | @SocketPort 41 | fun provideSocketPort(): String = BuildConfig.SOCKET_PORT 42 | 43 | @Provides 44 | @SocketBaseUrl 45 | fun provideSocketBaseUrl(@BaseUrl baseUrl: String, @SocketPort socketPort: String): String = 46 | "${baseUrl}:${socketPort}" 47 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/local/datasource/PreferenceDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.local.datasource 2 | 3 | import androidx.datastore.core.DataStore 4 | import androidx.datastore.preferences.core.* 5 | import com.devm7mdibrahim.data.datasource.PreferenceDataSource 6 | import kotlinx.coroutines.flow.Flow 7 | import kotlinx.coroutines.flow.map 8 | import javax.inject.Inject 9 | 10 | class PreferenceDataSourceImpl @Inject constructor(private val dataStore: DataStore) : 11 | PreferenceDataSource { 12 | 13 | override suspend fun getValue(key: String, default: Any?): Flow { 14 | return dataStore.data.map { 15 | when (default) { 16 | is String -> { 17 | it[stringPreferencesKey(key)] ?: default 18 | } 19 | 20 | is Double -> { 21 | it[doublePreferencesKey(key)] ?: default 22 | } 23 | 24 | is Int -> { 25 | it[intPreferencesKey(key)] ?: default 26 | } 27 | 28 | is Float -> { 29 | it[floatPreferencesKey(key)] ?: default 30 | } 31 | 32 | is Boolean -> { 33 | it[booleanPreferencesKey(key)] ?: default 34 | } 35 | 36 | is Long -> { 37 | it[longPreferencesKey(key)] ?: default 38 | } 39 | else -> default 40 | } 41 | } 42 | } 43 | 44 | override suspend fun setValue(key: String, value: Any?) { 45 | dataStore.edit { 46 | when (value) { 47 | is String -> { 48 | it[stringPreferencesKey(key)] = value 49 | } 50 | 51 | is Double -> { 52 | it[doublePreferencesKey(key)] = value 53 | } 54 | 55 | is Int -> { 56 | it[intPreferencesKey(key)] = value 57 | } 58 | 59 | is Float -> { 60 | it[floatPreferencesKey(key)] = value 61 | } 62 | 63 | is Boolean -> { 64 | it[booleanPreferencesKey(key)] = value 65 | } 66 | 67 | is Long -> { 68 | it[longPreferencesKey(key)] = value 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/local/utils/PreferenceConstants.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.local.utils 2 | 3 | object PreferenceConstants { 4 | const val PREFERENCE_NAME = "base_preferences" 5 | const val IS_ARABIC = "isArabic" 6 | const val LANGUAGE = "language" 7 | const val TOKEN = "token" 8 | const val USER_DATA = "userData" 9 | const val FIREBASE_TOKEN = "firebaseToken" 10 | const val ADDRESS = "address" 11 | const val LATITUDE = "latitude" 12 | const val LONGITUDE = "longitude" 13 | const val IS_ACTIVATED = "is_activated" 14 | const val IS_PROFILE_COMPLETED = "is_profile_completed" 15 | const val IS_FIRST_TIME = "is_first_time" 16 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/local/utils/SafeCacheCall.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.local.utils 2 | 3 | import com.devm7mdibrahim.domain.exceptions.LocalExceptions 4 | import com.devm7mdibrahim.domain.util.DataState 5 | import kotlinx.coroutines.CoroutineDispatcher 6 | import kotlinx.coroutines.TimeoutCancellationException 7 | import kotlinx.coroutines.flow.* 8 | import kotlinx.coroutines.withTimeout 9 | 10 | const val CACHE_TIMEOUT = 30000L 11 | 12 | suspend fun safeCacheCall( 13 | dispatcher: CoroutineDispatcher, 14 | cacheCall: suspend () -> T? 15 | ): Flow> = flow { 16 | withTimeout(CACHE_TIMEOUT) { 17 | val response = cacheCall.invoke() 18 | 19 | if (response != null) { 20 | emit(DataState.Success(response)) 21 | } else { 22 | emit(DataState.Error(LocalExceptions.UnknownException)) 23 | } 24 | } 25 | }.onStart { 26 | emit(DataState.Loading) 27 | }.catch { 28 | when (it) { 29 | is TimeoutCancellationException -> { 30 | emit(DataState.Error(LocalExceptions.TimeoutException)) 31 | } 32 | else -> { 33 | emit(DataState.Error(LocalExceptions.UnknownException)) 34 | } 35 | } 36 | }.flowOn(dispatcher) -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/remote/datasource/AuthDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.remote.datasource 2 | 3 | import com.devm7mdibrahim.data.datasource.AuthDataSource 4 | import com.devm7mdibrahim.data.remote.endpoints.AuthEndPoints 5 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.NetworkParams.AVATAR 6 | import com.devm7mdibrahim.data.utils.MultiPartUtil.prepareImagePart 7 | import com.devm7mdibrahim.data.utils.MultiPartUtil.toMultiPart 8 | import com.devm7mdibrahim.domain.entities.AuthData 9 | import com.devm7mdibrahim.domain.entities.BaseResponse 10 | import javax.inject.Inject 11 | 12 | class AuthDataSourceImpl @Inject constructor(private val authEndPoints: AuthEndPoints): AuthDataSource { 13 | 14 | override suspend fun login( 15 | phone: String, 16 | password: String, 17 | deviceId: String 18 | ): BaseResponse { 19 | return authEndPoints.login( 20 | phone = phone, 21 | password = password, 22 | deviceId = deviceId 23 | ) 24 | } 25 | 26 | override suspend fun register( 27 | name: String, 28 | phone: String, 29 | email: String, 30 | password: String, 31 | avatar: String? 32 | ): BaseResponse { 33 | return authEndPoints.register( 34 | name = name.toMultiPart(), 35 | phone = phone.toMultiPart(), 36 | email = email.toMultiPart(), 37 | password = password.toMultiPart(), 38 | avatar = avatar?.let { prepareImagePart(AVATAR, it) } 39 | ) 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/remote/datasource/ChatDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.remote.datasource 2 | 3 | import com.devm7mdibrahim.data.datasource.ChatDataSource 4 | import com.devm7mdibrahim.data.remote.endpoints.ChatEndPoints 5 | import com.devm7mdibrahim.domain.entities.* 6 | import javax.inject.Inject 7 | 8 | class ChatDataSourceImpl @Inject constructor(private val chatEndPoints: ChatEndPoints) : 9 | ChatDataSource { 10 | 11 | override suspend fun getRooms(): BaseResponse { 12 | return chatEndPoints.getRooms() 13 | } 14 | 15 | override suspend fun getChatMessages(roomId: Int): BaseResponse { 16 | return chatEndPoints.getChatMessages(roomId) 17 | } 18 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/remote/datasource/HomeDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.remote.datasource 2 | 3 | import com.devm7mdibrahim.data.datasource.HomeDataSource 4 | import com.devm7mdibrahim.data.remote.endpoints.HomeEndPoints 5 | import javax.inject.Inject 6 | 7 | class HomeDataSourceImpl @Inject constructor(private val homeEndPoints: HomeEndPoints) : 8 | HomeDataSource { 9 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/remote/endpoints/AuthEndPoints.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.remote.endpoints 2 | 3 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.EndPoints.LOGIN 4 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.EndPoints.REGISTER 5 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.NetworkParams.DEVICE_ID 6 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.NetworkParams.DEVICE_TYPE 7 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.NetworkParams.EMAIL 8 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.NetworkParams.NAME 9 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.NetworkParams.PASSWORD 10 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.NetworkParams.PHONE 11 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.NetworkParams.TYPE_ANDROID 12 | import com.devm7mdibrahim.domain.entities.AuthData 13 | import com.devm7mdibrahim.domain.entities.BaseResponse 14 | import okhttp3.MultipartBody 15 | import okhttp3.RequestBody 16 | import retrofit2.http.* 17 | 18 | interface AuthEndPoints { 19 | 20 | @FormUrlEncoded 21 | @POST(LOGIN) 22 | suspend fun login( 23 | @Field(PHONE) phone: String, 24 | @Field(PASSWORD) password: String, 25 | @Field(DEVICE_ID) deviceId: String, 26 | @Field(DEVICE_TYPE) deviceType: String = TYPE_ANDROID 27 | ): BaseResponse 28 | 29 | @Multipart 30 | @POST(REGISTER) 31 | suspend fun register( 32 | @Part(NAME) name: RequestBody, 33 | @Part(PHONE) phone: RequestBody, 34 | @Part(EMAIL) email: RequestBody, 35 | @Part(PASSWORD) password: RequestBody, 36 | @Part avatar: MultipartBody.Part?, 37 | ): BaseResponse 38 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/remote/endpoints/ChatEndPoints.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.remote.endpoints 2 | 3 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.EndPoints.CONVERSATION 4 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.EndPoints.ROOMS 5 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.EndPoints.ROOM_ID 6 | import com.devm7mdibrahim.domain.entities.BaseResponse 7 | import com.devm7mdibrahim.domain.entities.ChatResponse 8 | import com.devm7mdibrahim.domain.entities.RoomsResponse 9 | import retrofit2.http.Field 10 | import retrofit2.http.FormUrlEncoded 11 | import retrofit2.http.GET 12 | import retrofit2.http.POST 13 | 14 | interface ChatEndPoints { 15 | @GET(ROOMS) 16 | suspend fun getRooms(): BaseResponse 17 | 18 | @FormUrlEncoded 19 | @POST(CONVERSATION) 20 | suspend fun getChatMessages(@Field(ROOM_ID) roomId: Int): BaseResponse 21 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/remote/endpoints/HomeEndPoints.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.remote.endpoints 2 | 3 | interface HomeEndPoints { 4 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/remote/utils/NetworkConstants.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.remote.utils 2 | 3 | object NetworkConstants { 4 | const val LANGUAGE = "lang" 5 | const val BEARER = "Bearer " 6 | const val AUTHORIZATION = "Authorization" 7 | const val NETWORK_TIMEOUT = 60000L 8 | const val API_VERSION = "/api/" 9 | 10 | object Languages { 11 | const val ARABIC = "ar" 12 | const val ENGLISH = "en" 13 | } 14 | 15 | object RequestKeys{ 16 | const val SUCCESS = "success" 17 | const val FAIL = "fail" 18 | const val NEED_ACTIVE = "needActive" 19 | const val UN_AUTH = "unauthenticated" 20 | const val BLOCKED = "blocked" 21 | const val EXCEPTION = "exception" 22 | } 23 | 24 | object FailRequestCode { 25 | const val FAIL = 400 26 | const val UN_AUTH = 401 27 | const val BLOCKED = 423 28 | const val EXCEPTION = 500 29 | } 30 | 31 | object NetworkParams { 32 | const val PHONE = "phone" 33 | const val PASSWORD = "password" 34 | const val DEVICE_ID = "device_id" 35 | const val DEVICE_TYPE = "device_type" 36 | const val TYPE_ANDROID = "android" 37 | const val EMAIL = "email" 38 | const val MSG = "message" 39 | const val NAME = "name" 40 | const val AVATAR = "avatar" 41 | const val CODE = "code" 42 | const val LAT = "lat" 43 | const val LNG = "long" 44 | const val ADDRESS = "address" 45 | const val PAGE = "page" 46 | } 47 | 48 | object EndPoints { 49 | const val LOGIN = "login" 50 | const val REGISTER = "register" 51 | const val ROOMS = "rooms" 52 | const val CONVERSATION = "conversation" 53 | const val ROOM_ID = "room_id" 54 | } 55 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/remote/utils/SafeApiCall.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.remote.utils 2 | 3 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.FailRequestCode.BLOCKED 4 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.FailRequestCode.EXCEPTION 5 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.FailRequestCode.FAIL 6 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.FailRequestCode.UN_AUTH 7 | import com.devm7mdibrahim.data.remote.utils.NetworkConstants.NETWORK_TIMEOUT 8 | import com.devm7mdibrahim.domain.entities.BaseResponse 9 | import com.devm7mdibrahim.domain.exceptions.NetworkExceptions 10 | import com.devm7mdibrahim.domain.util.DataState 11 | import com.google.gson.Gson 12 | import kotlinx.coroutines.Dispatchers 13 | import kotlinx.coroutines.TimeoutCancellationException 14 | import kotlinx.coroutines.flow.* 15 | import kotlinx.coroutines.withTimeout 16 | import retrofit2.HttpException 17 | import java.io.IOException 18 | import java.net.UnknownHostException 19 | 20 | suspend fun safeApiCall( 21 | apiCall: suspend () -> T 22 | ): Flow> = flow { 23 | withTimeout(NETWORK_TIMEOUT) { 24 | val response = apiCall.invoke() 25 | emit(handleSuccess(response)) 26 | } 27 | }.onStart { 28 | emit(DataState.Loading) 29 | }.catch { 30 | emit(handleError(it)) 31 | }.flowOn(Dispatchers.IO) 32 | 33 | fun handleSuccess(response: T): DataState { 34 | return if (response != null) { 35 | DataState.Success(response) 36 | } else { 37 | DataState.Error(NetworkExceptions.UnknownException) 38 | } 39 | } 40 | 41 | fun handleError(it: Throwable): DataState { 42 | it.printStackTrace() 43 | return when (it) { 44 | is TimeoutCancellationException -> { 45 | DataState.Error(NetworkExceptions.TimeoutException) 46 | } 47 | 48 | is UnknownHostException -> { 49 | DataState.Error(NetworkExceptions.ConnectionException) 50 | } 51 | 52 | is IOException -> { 53 | DataState.Error(NetworkExceptions.UnknownException) 54 | } 55 | 56 | is HttpException -> { 57 | DataState.Error(convertErrorBody(it)) 58 | } 59 | 60 | else -> { 61 | DataState.Error(NetworkExceptions.UnknownException) 62 | } 63 | } 64 | } 65 | 66 | private fun convertErrorBody(throwable: HttpException): Exception { 67 | val errorBody = throwable.response()?.errorBody()?.charStream() 68 | val response = Gson().fromJson(errorBody, BaseResponse::class.java) 69 | return when (throwable.code()) { 70 | FAIL -> NetworkExceptions.CustomException(response.msg) 71 | UN_AUTH, BLOCKED -> NetworkExceptions.AuthorizationException 72 | EXCEPTION -> NetworkExceptions.ServerException 73 | else -> NetworkExceptions.UnknownException 74 | } 75 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/repository/AuthRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.repository 2 | 3 | import com.devm7mdibrahim.data.datasource.AuthDataSource 4 | import com.devm7mdibrahim.data.remote.utils.safeApiCall 5 | import com.devm7mdibrahim.domain.entities.AuthData 6 | import com.devm7mdibrahim.domain.entities.BaseResponse 7 | import com.devm7mdibrahim.domain.repository.AuthRepository 8 | import com.devm7mdibrahim.domain.util.DataState 9 | import kotlinx.coroutines.flow.Flow 10 | import kotlinx.coroutines.flow.flow 11 | import javax.inject.Inject 12 | 13 | class AuthRepositoryImpl @Inject constructor(private val authDataSource: AuthDataSource) : 14 | AuthRepository { 15 | override suspend fun login( 16 | phone: String, 17 | password: String, 18 | deviceId: String 19 | ): Flow>> = safeApiCall { 20 | authDataSource.login( 21 | phone = phone, 22 | password = password, 23 | deviceId = deviceId 24 | ) 25 | } 26 | 27 | override suspend fun register( 28 | name: String, 29 | phone: String, 30 | email: String, 31 | password: String, 32 | avatar: String? 33 | ): Flow>> = safeApiCall { 34 | authDataSource.register( 35 | name = name, 36 | phone = phone, 37 | email = email, 38 | password = password, 39 | avatar = avatar 40 | ) 41 | } 42 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/repository/ChatRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.repository 2 | 3 | import com.devm7mdibrahim.data.datasource.ChatDataSource 4 | import com.devm7mdibrahim.data.datasource.SocketDataSource 5 | import com.devm7mdibrahim.data.remote.utils.safeApiCall 6 | import com.devm7mdibrahim.domain.entities.BaseResponse 7 | import com.devm7mdibrahim.domain.entities.ChatResponse 8 | import com.devm7mdibrahim.domain.entities.RoomsResponse 9 | import com.devm7mdibrahim.domain.repository.ChatRepository 10 | import com.devm7mdibrahim.domain.util.DataState 11 | import kotlinx.coroutines.flow.Flow 12 | import kotlinx.coroutines.flow.collect 13 | import kotlinx.coroutines.flow.emitAll 14 | import kotlinx.coroutines.flow.flow 15 | import org.json.JSONObject 16 | import javax.inject.Inject 17 | 18 | class ChatRepositoryImpl @Inject constructor( 19 | private val chatDataSource: ChatDataSource, 20 | private val socketDataSource: SocketDataSource 21 | ) : ChatRepository { 22 | 23 | override suspend fun getRooms(): Flow>> = safeApiCall { 24 | chatDataSource.getRooms() 25 | } 26 | 27 | override suspend fun getChatMessages(roomId: Int): Flow>> = 28 | safeApiCall { 29 | chatDataSource.getChatMessages(roomId) 30 | } 31 | 32 | 33 | override fun connectSocket(): Flow = flow { 34 | socketDataSource.connectSocket().collect { 35 | emit(it) 36 | } 37 | } 38 | 39 | override fun disconnectSocket() = socketDataSource.disconnectSocket() 40 | 41 | override suspend fun addUser(key: String, jsonObject: JSONObject) { 42 | socketDataSource.setEmit(key, jsonObject) 43 | } 44 | 45 | override suspend fun exitChat(key: String, jsonObject: JSONObject) { 46 | socketDataSource.setEmit(key, jsonObject) 47 | } 48 | 49 | override suspend fun sendMessage(key: String, jsonObject: JSONObject) { 50 | socketDataSource.setEmit(key, jsonObject) 51 | } 52 | 53 | override suspend fun onMessageReceived(key: String): Flow> = flow { 54 | emitAll(socketDataSource.openChannel(key)) 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/repository/HomeRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.repository 2 | 3 | import com.devm7mdibrahim.data.datasource.HomeDataSource 4 | import com.devm7mdibrahim.domain.repository.HomeRepository 5 | import javax.inject.Inject 6 | 7 | class HomeRepositoryImpl @Inject constructor(private val homeDataSource: HomeDataSource) : 8 | HomeRepository { 9 | 10 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/socket/datasource/SocketDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.socket.datasource 2 | 3 | import com.devm7mdibrahim.data.datasource.SocketDataSource 4 | import io.socket.client.Socket 5 | import kotlinx.coroutines.GlobalScope 6 | import kotlinx.coroutines.flow.Flow 7 | import kotlinx.coroutines.flow.MutableStateFlow 8 | import kotlinx.coroutines.flow.collect 9 | import kotlinx.coroutines.flow.flow 10 | import kotlinx.coroutines.launch 11 | import org.json.JSONObject 12 | import javax.inject.Inject 13 | 14 | class SocketDataSourceImpl @Inject constructor( 15 | private val socket: Socket 16 | ) : SocketDataSource { 17 | 18 | private val connectFlow = MutableStateFlow(false) 19 | private val messagesFlow = MutableStateFlow(emptyList()) 20 | 21 | override fun connectSocket(): Flow = flow { 22 | if (!socket.connected()) { 23 | socket.connect() 24 | 25 | socket.on(Socket.EVENT_CONNECT) { 26 | GlobalScope.launch { connectFlow.emit(true) } 27 | socket.off(Socket.EVENT_CONNECT) 28 | } 29 | 30 | socket.on(Socket.EVENT_CONNECT_ERROR) { connectSocket() } 31 | 32 | } else { 33 | GlobalScope.launch { connectFlow.emit(true) } 34 | } 35 | 36 | connectFlow.collect { 37 | emit(it) 38 | } 39 | } 40 | 41 | override fun disconnectSocket() { 42 | if (socket.connected()) { 43 | socket.disconnect() 44 | } 45 | 46 | GlobalScope.launch { messagesFlow.emit(emptyList()) } 47 | } 48 | 49 | override fun openChannel( 50 | channel: String 51 | ): Flow> = flow { 52 | socket.off() 53 | socket.on(channel) { 54 | GlobalScope.launch { 55 | it.map { it.toString() }.let { messages -> 56 | messagesFlow.emit(messages) 57 | } 58 | } 59 | } 60 | 61 | messagesFlow.collect { 62 | emit(it) 63 | } 64 | } 65 | 66 | override fun setEmit( 67 | emitType: String, 68 | jsonObject: JSONObject? 69 | ) { 70 | if (socket.connected()) { 71 | socket.emit(emitType, jsonObject) 72 | } else { 73 | GlobalScope.launch { 74 | connectSocket().collect { 75 | if (it) { 76 | socket.emit(emitType, jsonObject) 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /data/src/main/java/com/devm7mdibrahim/data/utils/MultiPartUtil.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.data.utils 2 | 3 | import okhttp3.MediaType 4 | import okhttp3.MultipartBody 5 | import okhttp3.RequestBody 6 | import java.io.File 7 | 8 | object MultiPartUtil { 9 | 10 | fun String.toMultiPart(): RequestBody { 11 | return RequestBody.create( 12 | MediaType.parse("text/plain"), 13 | this 14 | ) 15 | } 16 | 17 | fun prepareImagePart( 18 | partName: String, 19 | path: String 20 | ): MultipartBody.Part { 21 | val file = File(path) 22 | val requestFile = RequestBody.create( 23 | MediaType.parse("image/*"), 24 | file 25 | ) 26 | return MultipartBody.Part.createFormData(partName, partName, requestFile) 27 | } 28 | 29 | fun preparePDFPart( 30 | partName: String, 31 | path: String 32 | ): MultipartBody.Part { 33 | val file = File(path) 34 | val requestFile = RequestBody.create( 35 | MediaType.parse("pdf/*"), 36 | file 37 | ) 38 | return MultipartBody.Part.createFormData(partName, partName, requestFile) 39 | } 40 | 41 | fun prepareAudioPart( 42 | partName: String, 43 | path: String 44 | ): MultipartBody.Part { 45 | val file = File(path) 46 | val requestFile = RequestBody.create( 47 | MediaType.parse("audio/*"), 48 | file 49 | ) 50 | return MultipartBody.Part.createFormData(partName, partName, requestFile) 51 | } 52 | 53 | 54 | fun prepareVideoPart( 55 | partName: String, 56 | path: String 57 | ): MultipartBody.Part { 58 | val file = File(path) 59 | val requestFile = RequestBody.create( 60 | MediaType.parse("video/*"), 61 | file 62 | ) 63 | return MultipartBody.Part.createFormData(partName, partName, requestFile) 64 | } 65 | } -------------------------------------------------------------------------------- /domain/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /domain/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'org.jetbrains.kotlin.jvm' 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_1_8 8 | targetCompatibility = JavaVersion.VERSION_1_8 9 | } 10 | 11 | dependencies { 12 | implementation Dependencies.javaxAnnotation 13 | implementation Dependencies.javaxInject 14 | 15 | implementation Dependencies.coroutine 16 | implementation Dependencies.coroutine_android 17 | 18 | implementation Dependencies.moshi_kotlin 19 | api Dependencies.json_org 20 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/entities/AuthData.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.entities 2 | 3 | data class AuthData( 4 | val token: String, 5 | val user: UserData 6 | ) 7 | 8 | data class UserData( 9 | val name: String, 10 | val phone: String, 11 | val email: String 12 | ) 13 | -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/entities/BaseResponse.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.entities 2 | 3 | import com.squareup.moshi.Json 4 | import com.squareup.moshi.JsonClass 5 | import java.io.Serializable 6 | 7 | @JsonClass(generateAdapter = true) 8 | data class BaseResponse( 9 | @Json(name = "key") val key: String, 10 | @Json(name = "data") val data: T?, 11 | @Json(name = "msg") val msg: String, 12 | @Json(name = "code") val code: Int, 13 | ) : Serializable -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/entities/ChatResponse.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.entities 2 | 3 | data class ChatResponse( 4 | val messages: List 5 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/entities/MessagesItem.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.entities 2 | 3 | import com.squareup.moshi.Json 4 | 5 | data class MessagesItem( 6 | @Json(name = "sender_type") val senderType: String 7 | ) 8 | 9 | -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/entities/Pagination.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.entities 2 | 3 | import com.squareup.moshi.Json 4 | 5 | data class Pagination( 6 | @Json(name = "total") val total: Int = 0, 7 | @Json(name = "count") val count: Int = 0, 8 | @Json(name = "per_page") val perPage: Int = 0, 9 | @Json(name = "next_page_url") val nextPageUrl: String = "", 10 | @Json(name = "perv_page_url") val pervPageUrl: String = "", 11 | @Json(name = "current_page") val currentPage: Int = 0, 12 | @Json(name = "total_pages") val totalPages: Int = 0 13 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/entities/RoomsItem.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.entities 2 | 3 | data class RoomsItem( 4 | val id: Int 5 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/entities/RoomsResponse.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.entities 2 | 3 | data class RoomsResponse ( 4 | val rooms: List 5 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/exceptions/LocalExceptions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.exceptions 2 | 3 | sealed class LocalExceptions : Exception() { 4 | object UnknownException : LocalExceptions() 5 | object TimeoutException : LocalExceptions() 6 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/exceptions/NetworkExceptions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.exceptions 2 | 3 | sealed class NetworkExceptions : Exception() { 4 | object UnknownException : NetworkExceptions() 5 | object ServerException : NetworkExceptions() 6 | object NotFoundException : NetworkExceptions() 7 | object TimeoutException : NetworkExceptions() 8 | object ConnectionException : NetworkExceptions() 9 | object AuthorizationException : NetworkExceptions() 10 | data class CustomException(val msg: String) : NetworkExceptions() 11 | data class NeedActiveException(val msg: String) : NetworkExceptions() 12 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/exceptions/ValidationException.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.exceptions 2 | 3 | sealed class ValidationException: Exception(){ 4 | object InValidPhoneException : ValidationException() 5 | object InValidNameException : ValidationException() 6 | object InValidPriceException : ValidationException() 7 | object InValidTextException : ValidationException() 8 | object InValidPasswordException : ValidationException() 9 | object InValidConfirmationPasswordException : ValidationException() 10 | object InValidEmailAddressException : ValidationException() 11 | object InValidCountryIsoException : ValidationException() 12 | object InValidDeviceIdException : ValidationException() 13 | object InValidVerificationCodeException : ValidationException() 14 | } 15 | -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/repository/AuthRepository.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.repository 2 | 3 | import com.devm7mdibrahim.domain.entities.AuthData 4 | import com.devm7mdibrahim.domain.entities.BaseResponse 5 | import com.devm7mdibrahim.domain.util.DataState 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | interface AuthRepository { 9 | 10 | suspend fun login( 11 | phone: String, 12 | password: String, 13 | deviceId: String 14 | ): Flow>> 15 | 16 | suspend fun register( 17 | name: String, 18 | phone: String, 19 | email: String, 20 | password: String, 21 | avatar: String? 22 | ): Flow>> 23 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/repository/ChatRepository.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.repository 2 | 3 | import com.devm7mdibrahim.domain.entities.BaseResponse 4 | import com.devm7mdibrahim.domain.entities.ChatResponse 5 | import com.devm7mdibrahim.domain.entities.RoomsResponse 6 | import com.devm7mdibrahim.domain.util.DataState 7 | import kotlinx.coroutines.flow.Flow 8 | import org.json.JSONObject 9 | 10 | interface ChatRepository { 11 | 12 | suspend fun getRooms(): Flow>> 13 | suspend fun getChatMessages(roomId: Int): Flow>> 14 | 15 | fun connectSocket(): Flow 16 | fun disconnectSocket() 17 | 18 | suspend fun addUser(key: String, jsonObject: JSONObject) 19 | suspend fun exitChat(key: String, jsonObject: JSONObject) 20 | 21 | suspend fun sendMessage(key: String, jsonObject: JSONObject) 22 | suspend fun onMessageReceived(key: String): Flow> 23 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/repository/HomeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.repository 2 | 3 | interface HomeRepository { 4 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/repository/PreferenceRepository.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.repository 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | 5 | interface PreferenceRepository { 6 | suspend fun getLanguage(): Flow 7 | suspend fun setLanguage(lang: String) 8 | 9 | suspend fun getIsArabicLanguage(): Flow 10 | 11 | suspend fun getToken():Flow 12 | suspend fun setToken(mToken: String) 13 | 14 | suspend fun getUserData(): Flow 15 | suspend fun setUserData(userData: String) 16 | 17 | suspend fun getFirebaseToken(): Flow 18 | suspend fun setFirebaseToken(token: String) 19 | 20 | suspend fun getAddress(): Flow 21 | suspend fun setAddress(address: String) 22 | 23 | suspend fun getLatitude(): Flow 24 | suspend fun setLatitude(latitude: Double) 25 | 26 | suspend fun getLongitude(): Flow 27 | suspend fun setLongitude(longitude: Double) 28 | 29 | suspend fun getIsActivated(): Flow 30 | suspend fun setIsActivated(active: Boolean) 31 | 32 | suspend fun getIsProfileCompleted(): Flow 33 | suspend fun setIsProfileCompleted(completed: Boolean) 34 | 35 | suspend fun getIsFirstTime(): Flow 36 | suspend fun setIsFirstTime(firstTime: Boolean) 37 | 38 | suspend fun onLogout() 39 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/usecases/LoginUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.usecases 2 | 3 | import com.devm7mdibrahim.domain.entities.AuthData 4 | import com.devm7mdibrahim.domain.entities.BaseResponse 5 | import com.devm7mdibrahim.domain.exceptions.ValidationException 6 | import com.devm7mdibrahim.domain.repository.AuthRepository 7 | import com.devm7mdibrahim.domain.util.CommonValidation.isValidPassword 8 | import com.devm7mdibrahim.domain.util.CommonValidation.isValidPhone 9 | import com.devm7mdibrahim.domain.util.DataState 10 | import kotlinx.coroutines.flow.Flow 11 | import kotlinx.coroutines.flow.emitAll 12 | import kotlinx.coroutines.flow.flow 13 | import javax.inject.Inject 14 | 15 | class LoginUseCase @Inject constructor( 16 | private val authRepository: AuthRepository 17 | ) { 18 | suspend operator fun invoke( 19 | phone: String, 20 | password: String, 21 | deviceId: String 22 | ): Flow>> = flow { 23 | when { 24 | !phone.isValidPhone() -> emit(DataState.Error(ValidationException.InValidPhoneException)) 25 | !password.isValidPassword() -> emit(DataState.Error(ValidationException.InValidPasswordException)) 26 | else -> emitAll( 27 | authRepository.login( 28 | phone = phone, 29 | password = password, 30 | deviceId = deviceId 31 | ) 32 | ) 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/usecases/RegisterUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.usecases 2 | 3 | import com.devm7mdibrahim.domain.entities.AuthData 4 | import com.devm7mdibrahim.domain.entities.BaseResponse 5 | import com.devm7mdibrahim.domain.exceptions.ValidationException 6 | import com.devm7mdibrahim.domain.repository.AuthRepository 7 | import com.devm7mdibrahim.domain.util.CommonValidation.isValidName 8 | import com.devm7mdibrahim.domain.util.CommonValidation.isValidPassword 9 | import com.devm7mdibrahim.domain.util.CommonValidation.isValidPhone 10 | import com.devm7mdibrahim.domain.util.DataState 11 | import kotlinx.coroutines.flow.Flow 12 | import kotlinx.coroutines.flow.emitAll 13 | import kotlinx.coroutines.flow.flow 14 | import javax.inject.Inject 15 | 16 | class RegisterUseCase @Inject constructor( 17 | private val authRepository: AuthRepository 18 | ) { 19 | suspend operator fun invoke( 20 | name: String, 21 | phone: String, 22 | email: String, 23 | password: String, 24 | avatar: String? 25 | ): Flow>> = flow { 26 | when { 27 | !name.isValidName() -> emit(DataState.Error(ValidationException.InValidNameException)) 28 | !phone.isValidPhone() -> emit(DataState.Error(ValidationException.InValidPhoneException)) 29 | !email.isValidPhone() -> emit(DataState.Error(ValidationException.InValidEmailAddressException)) 30 | !password.isValidPassword() -> emit(DataState.Error(ValidationException.InValidPasswordException)) 31 | else -> emitAll( 32 | authRepository.register( 33 | name = name, 34 | phone = phone, 35 | email = email, 36 | password = password, 37 | avatar = avatar 38 | ) 39 | ) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/util/CommonValidation.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.util 2 | 3 | import java.util.regex.Pattern 4 | 5 | object CommonValidation { 6 | 7 | fun String?.isValidPhone(): Boolean { 8 | return this?.isNotEmpty() == true && this.length >= 9 9 | } 10 | 11 | fun String?.isValidName(): Boolean { 12 | return this?.isNotEmpty() == true && this.length >= 3 13 | } 14 | 15 | fun String?.isValidText(): Boolean { 16 | return this?.isNotEmpty() == true && this.length >= 3 17 | } 18 | 19 | fun String?.isValidPassword(): Boolean { 20 | return this?.isNotEmpty() == true && this.length >= 6 21 | } 22 | 23 | fun String?.isValidPrice(): Boolean { 24 | return this?.isNotEmpty() == true 25 | } 26 | 27 | fun String?.isValidConfirmPassword(password: String): Boolean { 28 | return this?.isNotEmpty() == true && this == password 29 | } 30 | 31 | fun String?.isValidEmailAddress(): Boolean { 32 | val regex: Pattern = Pattern.compile( 33 | "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", 34 | Pattern.CASE_INSENSITIVE 35 | ) 36 | return regex.matcher(this.toString()).matches() 37 | } 38 | 39 | fun String?.isValidCountryIso(): Boolean { 40 | return !this.isNullOrEmpty() 41 | } 42 | 43 | fun String?.isValidDeviceId(): Boolean { 44 | return !this.isNullOrEmpty() 45 | } 46 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/util/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.util 2 | 3 | object Constants { 4 | const val SUCCESS = "success" 5 | const val FAIL = "fail" 6 | const val ACTIVE = "active" 7 | const val BLOCK = "block" 8 | const val PENDING = "pending" 9 | 10 | const val SUCCESS_CODE = 200 11 | const val FAIL_CODE = 401 12 | const val UNAUTHORIZED_CODE = 419 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/devm7mdibrahim/domain/util/DataState.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.domain.util 2 | 3 | sealed class DataState { 4 | data class Success(val data: T) : DataState() 5 | data class Error(val throwable: Throwable) : DataState() 6 | object Loading : DataState() 7 | object Idle : DataState() 8 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | android.enableJetifier=true 19 | android.useNewApkCreator=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | # Enables namespacing of each library's R class so that its R class includes only the 23 | # resources declared in the library itself and none from the library's dependencies, 24 | # thereby reducing the size of the R class for that library 25 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jan 31 12:59:52 EET 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /presentation/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /presentation/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-kapt' 5 | id 'dagger.hilt.android.plugin' 6 | id 'androidx.navigation.safeargs.kotlin' 7 | } 8 | 9 | android { 10 | compileSdk 31 11 | 12 | defaultConfig { 13 | minSdk 21 14 | targetSdk 31 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | kotlinOptions { 30 | jvmTarget = '1.8' 31 | } 32 | 33 | buildFeatures { 34 | viewBinding true 35 | } 36 | } 37 | 38 | dependencies { 39 | 40 | implementation project(':domain') 41 | implementation project(':utils') 42 | 43 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 44 | implementation 'androidx.core:core-ktx:1.7.0' 45 | implementation 'androidx.appcompat:appcompat:1.4.1' 46 | implementation 'com.google.android.material:material:1.5.0' 47 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 48 | 49 | api Dependencies.navigation_fragment 50 | api Dependencies.navigation_ui 51 | 52 | api 'com.akexorcist:localization:1.2.10' 53 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1' 54 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1' 55 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1' 56 | 57 | implementation Compilers.hilt_android 58 | kapt Compilers.hilt_android_compiler 59 | kapt Compilers.hilt_android_lifecycle_compiler 60 | } -------------------------------------------------------------------------------- /presentation/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/presentation/consumer-rules.pro -------------------------------------------------------------------------------- /presentation/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 -------------------------------------------------------------------------------- /presentation/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.base 2 | 3 | import com.akexorcist.localizationactivity.ui.LocalizationActivity 4 | 5 | abstract class BaseActivity : LocalizationActivity() { 6 | 7 | override fun onSupportNavigateUp(): Boolean { 8 | onBackPressed() 9 | return true 10 | } 11 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/base/BaseBottomSheetFragment.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.base 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.viewbinding.ViewBinding 8 | import com.devm7mdibrahim.domain.entities.BaseResponse 9 | import com.devm7mdibrahim.domain.exceptions.NetworkExceptions 10 | import com.devm7mdibrahim.domain.util.DataState 11 | import com.devm7mdibrahim.presentation.utils.getIsCommonException 12 | import com.devm7mdibrahim.utils.common.ProgressUtil 13 | import com.devm7mdibrahim.utils.extensions.ToastType 14 | import com.devm7mdibrahim.utils.extensions.showToast 15 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 16 | import javax.inject.Inject 17 | 18 | abstract class BaseBottomSheetFragment(private val inflate: Inflate) : 19 | BottomSheetDialogFragment() { 20 | 21 | @Inject 22 | lateinit var progressUtil: ProgressUtil 23 | 24 | abstract val viewModel: BaseViewModel 25 | 26 | private var _binding: VB? = null 27 | val binding get() = _binding!! 28 | 29 | override fun onCreate(savedInstanceState: Bundle?) { 30 | super.onCreate(savedInstanceState) 31 | setStyle(STYLE_NORMAL, com.devm7mdibrahim.utils.R.style.bottomSheetDialogStyle) 32 | startObserver() 33 | } 34 | 35 | override fun onCreateView( 36 | inflater: LayoutInflater, 37 | container: ViewGroup?, 38 | savedInstanceState: Bundle? 39 | ): View? { 40 | if (_binding == null) { 41 | _binding = inflate.invoke(inflater, container, false) 42 | onCreateBinding() 43 | } 44 | 45 | handleClicks() 46 | return binding.root 47 | } 48 | 49 | open fun onCreateBinding() {} 50 | 51 | open fun handleClicks() {} 52 | 53 | open fun startObserver() {} 54 | 55 | override fun onDestroyView() { 56 | super.onDestroyView() 57 | _binding = null 58 | } 59 | 60 | protected fun DataState.applyCommonSideEffects( 61 | showLoading: Boolean = true, 62 | showSuccessToast: Boolean = true, 63 | onSuccess: (T) -> Unit = {} 64 | ) { 65 | when (this) { 66 | is DataState.Loading -> { 67 | if (showLoading) progressUtil.showProgress() 68 | } 69 | 70 | is DataState.Success -> { 71 | if (showSuccessToast) requireContext().showToast( 72 | (data as BaseResponse<*>).msg, 73 | ToastType.SUCCESS 74 | ) 75 | onSuccess(this.data) 76 | } 77 | 78 | is DataState.Error -> { 79 | progressUtil.hideProgress() 80 | handleError(throwable) 81 | } 82 | 83 | DataState.Idle -> { 84 | progressUtil.hideProgress() 85 | } 86 | } 87 | } 88 | 89 | private fun handleError(throwable: Throwable) { 90 | when (throwable) { 91 | is NetworkExceptions.CustomException -> { 92 | requireContext().showToast(throwable.msg) 93 | } 94 | 95 | else -> { 96 | requireContext().showToast(getString(throwable.getIsCommonException())) 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/base/BaseDialogFragment.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.base 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.DialogFragment 8 | import androidx.viewbinding.ViewBinding 9 | import com.devm7mdibrahim.domain.entities.BaseResponse 10 | import com.devm7mdibrahim.domain.exceptions.NetworkExceptions 11 | import com.devm7mdibrahim.domain.util.DataState 12 | import com.devm7mdibrahim.presentation.utils.getIsCommonException 13 | import com.devm7mdibrahim.utils.common.ProgressUtil 14 | import com.devm7mdibrahim.utils.extensions.ToastType 15 | import com.devm7mdibrahim.utils.extensions.showToast 16 | import javax.inject.Inject 17 | 18 | abstract class BaseDialogFragment(private val inflate: Inflate) : 19 | DialogFragment() { 20 | 21 | private var _binding: VB? = null 22 | val binding get() = _binding!! 23 | 24 | @Inject 25 | lateinit var progressUtil: ProgressUtil 26 | 27 | abstract val viewModel: BaseViewModel 28 | 29 | override fun onCreateView( 30 | inflater: LayoutInflater, 31 | container: ViewGroup?, 32 | savedInstanceState: Bundle? 33 | ): View? { 34 | if (_binding == null) { 35 | _binding = inflate.invoke(inflater, container, false) 36 | onCreateView() 37 | } 38 | 39 | return binding.root 40 | } 41 | 42 | override fun onCreate(savedInstanceState: Bundle?) { 43 | super.onCreate(savedInstanceState) 44 | startObserver() 45 | } 46 | 47 | open fun startObserver() { 48 | 49 | } 50 | 51 | open fun onCreateView() { 52 | 53 | } 54 | 55 | protected fun DataState.applyCommonSideEffects( 56 | showLoading: Boolean = true, 57 | showSuccessToast: Boolean = true, 58 | onSuccess: (T) -> Unit = {} 59 | ) { 60 | when (this) { 61 | is DataState.Loading -> { 62 | if (showLoading) progressUtil.showProgress() 63 | } 64 | 65 | is DataState.Success -> { 66 | if (showSuccessToast) requireContext().showToast( 67 | (data as BaseResponse<*>).msg, 68 | ToastType.SUCCESS 69 | ) 70 | onSuccess(this.data) 71 | } 72 | 73 | is DataState.Error -> { 74 | progressUtil.hideProgress() 75 | handleError(throwable) 76 | } 77 | 78 | DataState.Idle -> { 79 | progressUtil.hideProgress() 80 | } 81 | } 82 | } 83 | 84 | private fun handleError(throwable: Throwable) { 85 | when (throwable) { 86 | is NetworkExceptions.CustomException -> { 87 | requireContext().showToast(throwable.msg) 88 | } 89 | 90 | else -> { 91 | requireContext().showToast(getString(throwable.getIsCommonException())) 92 | } 93 | } 94 | } 95 | 96 | override fun onDestroyView() { 97 | super.onDestroyView() 98 | _binding = null 99 | } 100 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/base/BaseRecyclerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.base 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import androidx.recyclerview.widget.ListAdapter 5 | import androidx.viewbinding.ViewBinding 6 | 7 | abstract class BaseRecyclerAdapter> 8 | (callback: DiffUtil.ItemCallback) : 9 | ListAdapter(callback) { 10 | 11 | override fun onBindViewHolder(holder: VIEW_HOLDER, position: Int) { 12 | holder.doBindings((getItem(position))) 13 | holder.bind() 14 | } 15 | 16 | override fun submitList(items: List?) { 17 | super.submitList(items ?: emptyList()) 18 | } 19 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/base/BaseViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.base 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import androidx.viewbinding.ViewBinding 5 | 6 | abstract class BaseViewHolder constructor(viewBinding: VIEW_BINDING) : 7 | RecyclerView.ViewHolder(viewBinding.root) { 8 | 9 | private var item: MODEL? = null 10 | 11 | fun doBindings(data: MODEL?) { 12 | this.item = data 13 | } 14 | 15 | abstract fun bind() 16 | 17 | fun getRowItem(): MODEL? { 18 | return item 19 | } 20 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/base/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.base 2 | 3 | import androidx.lifecycle.ViewModel 4 | import com.devm7mdibrahim.domain.repository.PreferenceRepository 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import javax.inject.Inject 7 | 8 | @HiltViewModel 9 | open class BaseViewModel @Inject constructor(): ViewModel() { 10 | 11 | @Inject 12 | lateinit var preferenceRepository: PreferenceRepository 13 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/auth_cycle/activity/AuthActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.auth_cycle.activity 2 | 3 | import android.os.Bundle 4 | import com.devm7mdibrahim.presentation.R 5 | import com.devm7mdibrahim.presentation.base.BaseActivity 6 | import dagger.hilt.android.AndroidEntryPoint 7 | 8 | @AndroidEntryPoint 9 | class AuthActivity : BaseActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | setContentView(R.layout.activity_auth) 13 | } 14 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/auth_cycle/fragment/login/LoginFragment.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.auth_cycle.fragment.login 2 | 3 | import androidx.fragment.app.viewModels 4 | import androidx.lifecycle.lifecycleScope 5 | import com.devm7mdibrahim.domain.entities.AuthData 6 | import com.devm7mdibrahim.domain.exceptions.ValidationException 7 | import com.devm7mdibrahim.domain.util.DataState 8 | import com.devm7mdibrahim.presentation.R 9 | import com.devm7mdibrahim.presentation.base.BaseFragment 10 | import com.devm7mdibrahim.presentation.databinding.FragmentLoginBinding 11 | import com.devm7mdibrahim.utils.extensions.* 12 | import dagger.hilt.android.AndroidEntryPoint 13 | import kotlinx.coroutines.flow.first 14 | 15 | @AndroidEntryPoint 16 | class LoginFragment : BaseFragment(FragmentLoginBinding::inflate) { 17 | 18 | override val viewModel by viewModels() 19 | 20 | override fun startObserver() { 21 | super.startObserver() 22 | loginObserver() 23 | } 24 | 25 | override fun handleClicks() { 26 | super.handleClicks() 27 | 28 | binding.btnLogin.onClick { 29 | login() 30 | } 31 | } 32 | 33 | private fun login() { 34 | lifecycleScope.launchWhenCreated { 35 | viewModel.loginResponse.emit(DataState.Idle) 36 | viewModel.login( 37 | phone = binding.etPhone.fetchText(), 38 | password = binding.etPassword.fetchText(), 39 | deviceId = viewModel.preferenceRepository.getFirebaseToken().first(), 40 | ) 41 | } 42 | } 43 | 44 | private fun loginObserver() { 45 | collectLifecycleFlow(viewModel.loginResponse) { 46 | when (it) { 47 | is DataState.Error -> { 48 | when (it.throwable) { 49 | is ValidationException.InValidPhoneException -> { 50 | requireContext().showToast(getString(R.string.error_invalid_phone)) 51 | } 52 | is ValidationException.InValidPasswordException -> { 53 | requireContext().showToast(getString(R.string.error_invalid_password)) 54 | } 55 | else -> { 56 | it.applyCommonSideEffects() 57 | } 58 | } 59 | } 60 | else -> { 61 | it.applyCommonSideEffects { response -> 62 | response.data?.let { userData -> saveUserData(userData) } 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | private fun saveUserData(data: AuthData) { 70 | lifecycleScope.launchWhenCreated { 71 | viewModel.preferenceRepository.setToken(data.token) 72 | viewModel.preferenceRepository.setUserData(data.user.toJson()) 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/auth_cycle/fragment/login/LoginViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.auth_cycle.fragment.login 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import com.devm7mdibrahim.domain.entities.AuthData 5 | import com.devm7mdibrahim.domain.entities.BaseResponse 6 | import com.devm7mdibrahim.domain.usecases.LoginUseCase 7 | import com.devm7mdibrahim.domain.util.DataState 8 | import com.devm7mdibrahim.presentation.base.BaseViewModel 9 | import dagger.hilt.android.lifecycle.HiltViewModel 10 | import kotlinx.coroutines.flow.MutableStateFlow 11 | import kotlinx.coroutines.flow.launchIn 12 | import kotlinx.coroutines.flow.onEach 13 | import kotlinx.coroutines.launch 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | class LoginViewModel @Inject constructor(private val loginUseCase: LoginUseCase) : BaseViewModel() { 18 | 19 | private val _loginResponse = 20 | MutableStateFlow>>(DataState.Idle) 21 | val loginResponse: MutableStateFlow>> 22 | get() = _loginResponse 23 | 24 | fun login(phone: String, password: String, deviceId: String) { 25 | viewModelScope.launch { 26 | loginUseCase( 27 | phone = phone, 28 | password = password, 29 | deviceId = deviceId 30 | ).onEach { 31 | _loginResponse.value = it 32 | }.launchIn(viewModelScope) 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/auth_cycle/fragment/register/RegisterFragment.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.auth_cycle.fragment.register 2 | 3 | import androidx.fragment.app.viewModels 4 | import androidx.lifecycle.lifecycleScope 5 | import com.devm7mdibrahim.domain.exceptions.ValidationException 6 | import com.devm7mdibrahim.domain.util.DataState 7 | import com.devm7mdibrahim.presentation.R 8 | import com.devm7mdibrahim.presentation.base.BaseFragment 9 | import com.devm7mdibrahim.presentation.databinding.FragmentRegisterBinding 10 | import com.devm7mdibrahim.utils.extensions.onPrintLog 11 | import com.devm7mdibrahim.utils.extensions.showToast 12 | import dagger.hilt.android.AndroidEntryPoint 13 | import kotlinx.coroutines.flow.collect 14 | 15 | @AndroidEntryPoint 16 | class RegisterFragment : BaseFragment(FragmentRegisterBinding::inflate) { 17 | 18 | private var avatar: String? = null 19 | override val viewModel by viewModels() 20 | 21 | override fun startObserver() { 22 | super.startObserver() 23 | registerListener() 24 | } 25 | 26 | private fun register() { 27 | lifecycleScope.launchWhenCreated { 28 | viewModel.registerResponse.emit(DataState.Idle) 29 | viewModel.register( 30 | name = "Mohamed", 31 | phone = "01024510687", 32 | email = "dev.m7mdibrahim@gmail.com", 33 | password = "123456", 34 | avatar = avatar 35 | ) 36 | } 37 | } 38 | 39 | private fun registerListener() { 40 | lifecycleScope.launchWhenCreated { 41 | viewModel.registerResponse.collect { it -> 42 | when (it) { 43 | is DataState.Error -> { 44 | when (it.throwable) { 45 | is ValidationException.InValidPhoneException -> { 46 | requireContext().showToast(getString(R.string.error_invalid_phone)) 47 | } 48 | is ValidationException.InValidPasswordException -> { 49 | requireContext().showToast(getString(R.string.error_invalid_password)) 50 | } 51 | is ValidationException.InValidNameException -> { 52 | requireContext().showToast(getString(R.string.error_invalid_name)) 53 | } 54 | is ValidationException.InValidEmailAddressException -> { 55 | requireContext().showToast(getString(R.string.error_invalid_email)) 56 | } 57 | else -> { 58 | it.applyCommonSideEffects() 59 | } 60 | } 61 | } 62 | else -> { 63 | it.applyCommonSideEffects { 64 | it.data?.onPrintLog("userData") 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/auth_cycle/fragment/register/RegisterViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.auth_cycle.fragment.register 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import com.devm7mdibrahim.domain.entities.AuthData 5 | import com.devm7mdibrahim.domain.entities.BaseResponse 6 | import com.devm7mdibrahim.domain.usecases.RegisterUseCase 7 | import com.devm7mdibrahim.domain.util.DataState 8 | import com.devm7mdibrahim.presentation.base.BaseViewModel 9 | import dagger.hilt.android.lifecycle.HiltViewModel 10 | import kotlinx.coroutines.flow.MutableStateFlow 11 | import kotlinx.coroutines.flow.launchIn 12 | import kotlinx.coroutines.flow.onEach 13 | import kotlinx.coroutines.launch 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | class RegisterViewModel @Inject constructor(private val registerUseCase: RegisterUseCase) : 18 | BaseViewModel() { 19 | 20 | private val _registerResponse = 21 | MutableStateFlow>>(DataState.Idle) 22 | val registerResponse: MutableStateFlow>> 23 | get() = _registerResponse 24 | 25 | fun register(name: String, phone: String, email: String, password: String, avatar: String?) { 26 | viewModelScope.launch { 27 | registerUseCase( 28 | name = name, 29 | phone = phone, 30 | email = email, 31 | password = password, 32 | avatar = avatar 33 | ).onEach { 34 | _registerResponse.value = it 35 | }.launchIn(viewModelScope) 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/home_cycle/activity/HomeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.home_cycle.activity 2 | 3 | import android.os.Bundle 4 | import com.devm7mdibrahim.presentation.R 5 | import com.devm7mdibrahim.presentation.base.BaseActivity 6 | import dagger.hilt.android.AndroidEntryPoint 7 | 8 | @AndroidEntryPoint 9 | class HomeActivity : BaseActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | setContentView(R.layout.activity_home) 13 | } 14 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/home_cycle/fragment/home_container/HomeContainerFragment.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.home_cycle.fragment.home_container 2 | 3 | import androidx.fragment.app.viewModels 4 | import androidx.navigation.Navigation 5 | import androidx.navigation.ui.NavigationUI 6 | import androidx.navigation.ui.setupWithNavController 7 | import com.devm7mdibrahim.presentation.R 8 | import com.devm7mdibrahim.presentation.base.BaseFragment 9 | import com.devm7mdibrahim.presentation.databinding.FragmentHomeContainerBinding 10 | import dagger.hilt.android.AndroidEntryPoint 11 | 12 | @AndroidEntryPoint 13 | class HomeContainerFragment : 14 | BaseFragment(FragmentHomeContainerBinding::inflate) { 15 | 16 | override val viewModel by viewModels() 17 | 18 | override fun onResume() { 19 | super.onResume() 20 | setupBottomNavigationView() 21 | } 22 | 23 | private fun setupBottomNavigationView() { 24 | val navController = Navigation.findNavController(requireActivity(), R.id.home_container) 25 | NavigationUI.setupWithNavController(binding.navView, navController) 26 | binding.navView.setupWithNavController(navController) 27 | } 28 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/home_cycle/fragment/home_container/HomeContainerViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.home_cycle.fragment.home_container 2 | 3 | import com.devm7mdibrahim.presentation.base.BaseViewModel 4 | import dagger.hilt.android.lifecycle.HiltViewModel 5 | import javax.inject.Inject 6 | 7 | @HiltViewModel 8 | class HomeContainerViewModel @Inject constructor() : BaseViewModel() { 9 | 10 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/home_cycle/fragment/home_container/home/HomeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.home_cycle.fragment.home_container.home 2 | 3 | import androidx.fragment.app.viewModels 4 | import com.devm7mdibrahim.presentation.base.BaseFragment 5 | import com.devm7mdibrahim.presentation.databinding.FragmentHomeBinding 6 | import dagger.hilt.android.AndroidEntryPoint 7 | 8 | @AndroidEntryPoint 9 | class HomeFragment : BaseFragment(FragmentHomeBinding::inflate) { 10 | 11 | override val viewModel by viewModels() 12 | 13 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/home_cycle/fragment/home_container/home/HomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.home_cycle.fragment.home_container.home 2 | 3 | import com.devm7mdibrahim.presentation.base.BaseViewModel 4 | import dagger.hilt.android.lifecycle.HiltViewModel 5 | import javax.inject.Inject 6 | 7 | @HiltViewModel 8 | class HomeViewModel @Inject constructor() : BaseViewModel() { 9 | 10 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/home_cycle/fragment/home_container/notifications/NotificationsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.home_cycle.fragment.home_container.notifications 2 | 3 | import androidx.fragment.app.viewModels 4 | import com.devm7mdibrahim.presentation.base.BaseFragment 5 | import com.devm7mdibrahim.presentation.databinding.FragmentNotificationsBinding 6 | import dagger.hilt.android.AndroidEntryPoint 7 | 8 | @AndroidEntryPoint 9 | class NotificationsFragment : BaseFragment(FragmentNotificationsBinding::inflate) { 10 | 11 | override val viewModel by viewModels() 12 | 13 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/home_cycle/fragment/home_container/notifications/NotificationsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.home_cycle.fragment.home_container.notifications 2 | 3 | import com.devm7mdibrahim.presentation.base.BaseViewModel 4 | import dagger.hilt.android.lifecycle.HiltViewModel 5 | import javax.inject.Inject 6 | 7 | @HiltViewModel 8 | class NotificationsViewModel @Inject constructor(): BaseViewModel() { 9 | 10 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/home_cycle/fragment/home_container/profile/ProfileFragment.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.home_cycle.fragment.home_container.profile 2 | 3 | import androidx.fragment.app.viewModels 4 | import com.devm7mdibrahim.presentation.base.BaseFragment 5 | import com.devm7mdibrahim.presentation.databinding.FragmentProfileBinding 6 | import dagger.hilt.android.AndroidEntryPoint 7 | 8 | @AndroidEntryPoint 9 | class ProfileFragment : BaseFragment(FragmentProfileBinding::inflate) { 10 | 11 | override val viewModel by viewModels() 12 | 13 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/home_cycle/fragment/home_container/profile/ProfileViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.home_cycle.fragment.home_container.profile 2 | 3 | import com.devm7mdibrahim.presentation.base.BaseViewModel 4 | import dagger.hilt.android.lifecycle.HiltViewModel 5 | import javax.inject.Inject 6 | 7 | @HiltViewModel 8 | class ProfileViewModel @Inject constructor() : BaseViewModel() { 9 | 10 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/splash_cycle/activity/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.splash_cycle.activity 2 | 3 | import android.annotation.SuppressLint 4 | import android.os.Bundle 5 | import com.devm7mdibrahim.presentation.R 6 | import com.devm7mdibrahim.presentation.base.BaseActivity 7 | import dagger.hilt.android.AndroidEntryPoint 8 | 9 | @SuppressLint("CustomSplashScreen") 10 | @AndroidEntryPoint 11 | class SplashActivity : BaseActivity() { 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | setContentView(R.layout.activity_splash) 16 | } 17 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/cycles/splash_cycle/fragment/SplashFragment.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.cycles.splash_cycle.fragment 2 | 3 | import android.content.Intent 4 | import androidx.fragment.app.viewModels 5 | import androidx.lifecycle.lifecycleScope 6 | import com.devm7mdibrahim.presentation.base.BaseFragment 7 | import com.devm7mdibrahim.presentation.base.BaseViewModel 8 | import com.devm7mdibrahim.presentation.cycles.auth_cycle.activity.AuthActivity 9 | import com.devm7mdibrahim.presentation.cycles.home_cycle.activity.HomeActivity 10 | import com.devm7mdibrahim.presentation.databinding.FragmentSplashBinding 11 | import dagger.hilt.android.AndroidEntryPoint 12 | import kotlinx.coroutines.delay 13 | import kotlinx.coroutines.flow.first 14 | 15 | @AndroidEntryPoint 16 | class SplashFragment : BaseFragment(FragmentSplashBinding::inflate) { 17 | 18 | override val viewModel by viewModels() 19 | 20 | override fun onResume() { 21 | super.onResume() 22 | 23 | lifecycleScope.launchWhenResumed { 24 | delay(2000) 25 | 26 | viewModel.preferenceRepository.getToken().first { token -> 27 | if (token.isNotEmpty()) { 28 | openHomeActivity() 29 | } else { 30 | openAuthActivity() 31 | } 32 | 33 | return@first true 34 | } 35 | } 36 | } 37 | 38 | private fun openHomeActivity() { 39 | Intent(requireActivity(), HomeActivity::class.java) 40 | .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK).also { 41 | startActivity(it) 42 | } 43 | } 44 | 45 | private fun openAuthActivity() { 46 | Intent(requireActivity(), AuthActivity::class.java) 47 | .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK).also { 48 | startActivity(it) 49 | } 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/utils/CommonErrorHandling.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.utils 2 | 3 | import com.devm7mdibrahim.domain.exceptions.NetworkExceptions 4 | import com.devm7mdibrahim.presentation.R 5 | 6 | 7 | fun Throwable.getIsCommonException(): Int { 8 | when (this) { 9 | is NetworkExceptions.ConnectionException -> { 10 | return R.string.error_connection 11 | } 12 | 13 | is NetworkExceptions.NotFoundException -> { 14 | return R.string.error_not_found 15 | } 16 | 17 | is NetworkExceptions.ServerException -> { 18 | return R.string.error_server 19 | } 20 | 21 | is NetworkExceptions.TimeoutException -> { 22 | return R.string.error_timeout 23 | } 24 | 25 | is NetworkExceptions.UnknownException -> { 26 | return R.string.error_unknown 27 | } 28 | 29 | else -> { 30 | return R.string.error_unknown 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /presentation/src/main/java/com/devm7mdibrahim/presentation/utils/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.presentation.utils 2 | 3 | object Constants { 4 | 5 | object ChatKeys{ 6 | const val SUBSCRIBE_ROOM = "subscribe-room" 7 | const val UNSUBSCRIBE_ROOM = "unsubscribe-room" 8 | 9 | const val USER_ID = "user_id" 10 | const val ROOM_ID = "room_id" 11 | 12 | const val MESSAGE = "message" 13 | const val MESSAGE_TYPE = "message_type" 14 | const val MESSAGE_TEXT = "text" 15 | const val SEND_MESSAGE = "sendChatMessage" 16 | const val RECEIVE_MESSAGE = "receiveChatMessage" 17 | } 18 | } -------------------------------------------------------------------------------- /presentation/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /presentation/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /presentation/src/main/res/anim/slide_in_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | -------------------------------------------------------------------------------- /presentation/src/main/res/anim/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | -------------------------------------------------------------------------------- /presentation/src/main/res/anim/slide_out_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | -------------------------------------------------------------------------------- /presentation/src/main/res/anim/slide_out_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | -------------------------------------------------------------------------------- /presentation/src/main/res/color/bottom_nav_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/activity_auth.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/activity_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/base_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 27 | 28 | 41 | 42 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/fragment_home_container.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 33 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/fragment_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 17 | 18 | 19 | 29 | 30 | 41 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/fragment_notifications.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/fragment_profile.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/fragment_register.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/layout/fragment_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /presentation/src/main/res/menu/home_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /presentation/src/main/res/navigation/auth_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /presentation/src/main/res/navigation/home_container_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 14 | 18 | -------------------------------------------------------------------------------- /presentation/src/main/res/navigation/home_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /presentation/src/main/res/navigation/splash_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /presentation/src/main/res/values-ar/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Base Modules 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 | هذا النص هو مثال لنص يمكن أن يستبدل في نفس المساحة، 30 | لقد تم توليد هذا النص من مولد النص العربى، 31 | هذا النص هو مثال لنص يمكن أن يستبدل في نفس المساحة، 32 | لقد تم توليد هذاالنص من مولد النص العربى، 33 | هذا النص هو مثال لنص يمكن أن يستبدل في نفس المساحة 34 | -------------------------------------------------------------------------------- /presentation/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FAEA27 5 | #99C056 6 | #FFD903 7 | #FFD903 8 | #99C056 9 | #FFFFFFFF 10 | 11 | 12 | #FAEA27 13 | #0B3434 14 | #99C056 15 | #8DA34E 16 | 17 | #77FAEA27 18 | 19 | 20 | #2C52A2 21 | #9FDAF7 22 | #FFD903 23 | #f2f2f2 24 | 25 | 26 | #e22b2b 27 | #fc8b8d 28 | #CDD4D9 29 | #f2f2f2 30 | #c4c4c4 31 | #a8a8a8 32 | #7d7d7d 33 | #5c5c5c 34 | 35 | #686868 36 | #EEEEEE 37 | #4FD477 38 | #E2DDD0 39 | #000 40 | #1F2327 41 | #8D8D8D 42 | #33FFD903 43 | #575757 44 | #EFEFEF 45 | #33EFEFEF 46 | #CB0529 47 | #EEEEEE 48 | #6B6B6B 49 | #DFE0E0 50 | #FFCE00 51 | #FBFAF8 52 | #FBFAF8 53 | #BA94C1 54 | #CDD4D9 55 | #9FFFFFFF 56 | #393939 57 | #E30613 58 | #FC0000 59 | #F6F6F6 60 | #F2F2F2 61 | #666767 62 | #707070 63 | #08324D 64 | #D31E37 65 | #464646 66 | #989898 67 | #82A94C 68 | #F8F8F8 69 | #FF0000 70 | 71 | -------------------------------------------------------------------------------- /presentation/src/main/res/values/google_maps_api.xml: -------------------------------------------------------------------------------- 1 | 2 | AIzaSyD5eKqs2_QBnxgMePLiEmBpz7WXr_aPuFA 3 | -------------------------------------------------------------------------------- /presentation/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Base Modules 3 | There are no chats 4 | OK 5 | Cancel 6 | SAR 7 | No internet connection 8 | Alert 9 | Are you sure you want to logout? 10 | You must login to continue 11 | Your account has been blocked or deleted. In the event of an error, please contact the application administration. 12 | 13 | Timeout, please try again 14 | Something went wrong, please try again 15 | Something went wrong, please try again later 16 | Something went wrong, please try again later 17 | No internet connection 18 | UnAuthorized user 19 | 20 | Invalid phone number 21 | Invalid user name 22 | Invalid password 23 | Invalid confirmation password 24 | Invalid email address 25 | Invalid device id 26 | Please enter the verification code before confirm 27 | 28 | This text is an example of a text that can be replaced in the same space, 29 | this text was generated from the Arabic text generator, 30 | this text is an example of text that can be replaced in the same space, 31 | this text was generated from the Arabic text generator, 32 | this text is an example of a text that can be replaced To replace in the same space, 33 | it has been taken over 34 | -------------------------------------------------------------------------------- /presentation/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 15 | 16 | 19 | 20 | 24 | 25 | 40 | 41 | -------------------------------------------------------------------------------- /presentation/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | 19 | 38 | 39 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "base-android" 2 | include ':app' 3 | include ':presentation' 4 | include ':data' 5 | include ':domain' 6 | include ':utils' -------------------------------------------------------------------------------- /utils/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /utils/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | android { 8 | compileSdk 31 9 | 10 | defaultConfig { 11 | minSdk 21 12 | targetSdk 31 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | consumerProguardFiles "consumer-rules.pro" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | kotlinOptions { 29 | jvmTarget = '1.8' 30 | } 31 | } 32 | 33 | dependencies { 34 | 35 | implementation Compilers.hilt_android 36 | kapt Compilers.hilt_android_compiler 37 | kapt Compilers.hilt_android_lifecycle_compiler 38 | 39 | implementation Dependencies.navigation_fragment 40 | implementation Dependencies.navigation_ui 41 | 42 | implementation Dependencies.firebase_analytics 43 | implementation Dependencies.firebase_crashlytics 44 | 45 | api 'androidx.core:core-ktx:1.7.0' 46 | api 'androidx.appcompat:appcompat:1.4.2' 47 | api 'com.google.android.material:material:1.6.1' 48 | api 'androidx.legacy:legacy-support-v4:1.0.0' 49 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1' 50 | 51 | api 'com.github.bumptech.glide:glide:4.12.0' 52 | kapt 'com.github.bumptech.glide:compiler:4.12.0' 53 | 54 | api "io.nlopez.smartlocation:rx:3.3.3" 55 | api 'com.karumi:dexter:6.2.2' 56 | api 'com.akexorcist:localization:1.2.10' 57 | api "org.greenrobot:eventbus:3.2.0" 58 | 59 | api 'com.tuyenmonkey:mkloader:1.4.0' 60 | api 'com.github.GrenderG:Toasty:1.5.2' 61 | api "de.hdodenhof:circleimageview:3.1.0" 62 | api 'com.makeramen:roundedimageview:2.3.0' 63 | api 'com.github.iwgang:countdownview:2.1.6' 64 | api 'com.alimuzaffar.lib:pinentryedittext:2.0.6' 65 | 66 | api "com.intuit.sdp:sdp-android:1.0.6" 67 | api "com.intuit.ssp:ssp-android:1.0.6" 68 | 69 | //google services 70 | api "com.google.android.gms:play-services-maps:18.0.2" 71 | api "com.google.maps:google-maps-services:0.1.20" 72 | api "com.google.android.gms:play-services-location:20.0.0" 73 | api "com.google.android.libraries.places:places:2.6.0" 74 | api "com.google.android.gms:play-services-places:17.0.0" 75 | implementation Dependencies.firebase_messaging 76 | } -------------------------------------------------------------------------------- /utils/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devm7mdibrahim/MVVM-CleanArchitecture/a9cec8c10c271bfa756863e2fa71b5977e802a0f/utils/consumer-rules.pro -------------------------------------------------------------------------------- /utils/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 -------------------------------------------------------------------------------- /utils/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/common/NetworkHelper.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.common 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import android.net.NetworkCapabilities 6 | import android.os.Build 7 | import dagger.hilt.android.qualifiers.ApplicationContext 8 | import javax.inject.Inject 9 | import javax.inject.Singleton 10 | 11 | @Singleton 12 | class NetworkHelper @Inject constructor(@ApplicationContext private val context: Context) { 13 | 14 | @Suppress("DEPRECATION") 15 | fun isNetworkConnected(): Boolean { 16 | var result = false 17 | val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 18 | 19 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 20 | val networkCapabilities = connectivityManager.activeNetwork ?: return false 21 | val activeNetwork = connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false 22 | 23 | result = when { 24 | activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true 25 | activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true 26 | activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true 27 | else -> false 28 | } 29 | 30 | } else { 31 | connectivityManager.run { 32 | connectivityManager.activeNetworkInfo?.run { 33 | result = when (type) { 34 | ConnectivityManager.TYPE_WIFI -> true 35 | ConnectivityManager.TYPE_MOBILE -> true 36 | ConnectivityManager.TYPE_ETHERNET -> true 37 | else -> false 38 | } 39 | 40 | } 41 | } 42 | } 43 | 44 | return result 45 | } 46 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/common/PaginationHelper.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.common 2 | 3 | import androidx.recyclerview.widget.LinearLayoutManager 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | 7 | abstract class PaginationHelper(var layoutManager: LinearLayoutManager) : 8 | RecyclerView.OnScrollListener() { 9 | 10 | abstract fun isLastPage(): Boolean 11 | 12 | abstract fun isLoading(): Boolean 13 | 14 | override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { 15 | 16 | recyclerView.let { super.onScrolled(it, dx, dy) } 17 | 18 | val visibleItemCount = layoutManager.childCount 19 | val totalItemCount = layoutManager.itemCount 20 | val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() 21 | 22 | if (dy > 0) 23 | if (!isLoading() && !isLastPage()) { 24 | if (visibleItemCount + firstVisibleItemPosition >= totalItemCount) 25 | if (visibleItemCount + firstVisibleItemPosition >= totalItemCount && firstVisibleItemPosition >= 0) { 26 | loadMoreItems() 27 | } 28 | } 29 | } 30 | 31 | abstract fun loadMoreItems() 32 | 33 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/common/ProgressUtil.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.common 2 | 3 | import android.app.AlertDialog 4 | import android.content.Context 5 | import android.graphics.Color 6 | import android.graphics.drawable.ColorDrawable 7 | import android.view.LayoutInflater 8 | import android.view.ViewGroup 9 | import android.view.Window 10 | import com.devm7mdibrahim.utils.R 11 | import javax.inject.Inject 12 | import javax.inject.Singleton 13 | 14 | @Singleton 15 | class ProgressUtil @Inject constructor(val context: Context) { 16 | 17 | 18 | private var dialog: AlertDialog? = null 19 | 20 | init { 21 | init() 22 | } 23 | 24 | private fun init() { 25 | dialog = AlertDialog.Builder(context).create() 26 | dialog?.apply { 27 | val inflate = LayoutInflater.from(context).inflate(R.layout.progress, findViewById(android.R.id.content),false) 28 | setView(inflate) 29 | requestWindowFeature(Window.FEATURE_NO_TITLE) 30 | setCancelable(false) 31 | } 32 | 33 | dialog?.window?.apply { 34 | setLayout( 35 | ViewGroup.LayoutParams.MATCH_PARENT, 36 | ViewGroup.LayoutParams.WRAP_CONTENT 37 | ) 38 | setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) 39 | } 40 | } 41 | 42 | 43 | fun showProgress() { 44 | if (dialog != null && dialog?.isShowing == false) { 45 | dialog?.show() 46 | } 47 | } 48 | 49 | 50 | fun hideProgress() { 51 | if (dialog?.isShowing == true) { 52 | dialog?.dismiss() 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/di/ActivityModule.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.di 2 | 3 | import android.app.Activity 4 | import com.devm7mdibrahim.utils.common.ProgressUtil 5 | import dagger.Module 6 | import dagger.Provides 7 | import dagger.hilt.InstallIn 8 | import dagger.hilt.android.components.ActivityComponent 9 | import dagger.hilt.android.scopes.ActivityScoped 10 | 11 | @Module 12 | @InstallIn(ActivityComponent::class) 13 | object ActivityModule { 14 | 15 | @Provides 16 | @ActivityScoped 17 | fun provideProgressUtil(activity: Activity): ProgressUtil { 18 | return ProgressUtil(activity) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/di/GlideModule.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.di 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import com.bumptech.glide.GlideBuilder 6 | import com.bumptech.glide.annotation.GlideModule 7 | 8 | import com.bumptech.glide.module.AppGlideModule 9 | 10 | 11 | @GlideModule 12 | class GlideModule : AppGlideModule() { 13 | override fun applyOptions(context: Context, builder: GlideBuilder) { 14 | super.applyOptions(context, builder) 15 | builder.setLogLevel(Log.ERROR) 16 | } 17 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/AlertExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import android.app.AlertDialog 4 | import android.content.Context 5 | import com.devm7mdibrahim.utils.R 6 | 7 | 8 | fun Context.showAlertDialog( 9 | title: String? = null, 10 | msg: String? = null, 11 | drawable: Int? = null, 12 | listener: (Boolean) -> Unit 13 | ) { 14 | AlertDialog.Builder(this) 15 | .setCancelable(false) 16 | .setTitle(title ?: "") 17 | .setMessage(msg ?: "") 18 | .setPositiveButton( 19 | R.string.text_ok 20 | ) { _, _ -> 21 | listener(true) 22 | } 23 | .setNegativeButton( 24 | R.string.text_cancel 25 | ) { dialog, _ -> 26 | listener(false) 27 | dialog.cancel() 28 | }.setIcon(drawable ?: android.R.drawable.ic_dialog_alert) 29 | .show() 30 | } 31 | 32 | fun Context.openLoginDialog(listener: (Boolean) -> Unit) { 33 | showAlertDialog( 34 | getString(R.string.text_alert), 35 | getString(R.string.text_are_must_login_to_continue) 36 | ) { 37 | listener(it) 38 | } 39 | } 40 | 41 | fun Context.openAccountDeletedDialog(listener: (Boolean) -> Unit) { 42 | showAlertDialog( 43 | getString(R.string.text_alert), 44 | getString(R.string.block_massage) 45 | ) { 46 | listener(it) 47 | } 48 | } 49 | 50 | fun Context.openLogoutDialog(listener: (Boolean) -> Unit) { 51 | showAlertDialog( 52 | getString(R.string.text_alert), 53 | getString(R.string.text_are_you_sure_logout) 54 | ) { 55 | listener(it) 56 | } 57 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/CommonExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import com.devm7mdibrahim.utils.R 6 | import com.google.gson.Gson 7 | 8 | 9 | fun Any.onPrintLog(tag: String = "App LOG ==>>> ") { 10 | Log.d(tag, Gson().toJson(this)) 11 | } 12 | 13 | fun Context.setPriceWithCurrency( 14 | price: String?, 15 | currency: String = getString(R.string.sar) 16 | ): String { 17 | if (price == null) return "" 18 | return buildString { 19 | append(price) 20 | append(" ") 21 | append(currency) 22 | } 23 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/ExceptionsExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import com.google.firebase.crashlytics.FirebaseCrashlytics 4 | 5 | fun Throwable.log() { 6 | printStackTrace() 7 | FirebaseCrashlytics.getInstance().recordException(this) 8 | } 9 | 10 | fun catch(action: () -> Unit) = 11 | try { 12 | action.invoke() 13 | } catch (throwable: Throwable) { 14 | throwable.log() 15 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/FirebaseExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import com.google.firebase.messaging.FirebaseMessaging 4 | 5 | fun getFirebaseToken(token : (String) -> Unit) { 6 | FirebaseMessaging.getInstance().token.addOnCompleteListener { 7 | if (it.isSuccessful) { 8 | token(it.result) 9 | } else { 10 | it.exception?.printStackTrace() 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/FragmentExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import androidx.lifecycle.Lifecycle 6 | import androidx.lifecycle.lifecycleScope 7 | import androidx.lifecycle.repeatOnLifecycle 8 | import kotlinx.coroutines.flow.Flow 9 | import kotlinx.coroutines.flow.collect 10 | import kotlinx.coroutines.launch 11 | 12 | fun Fragment.setFragmentResult(requestKey: String, result: Bundle) { 13 | parentFragmentManager.setFragmentResult(requestKey, result) 14 | } 15 | 16 | fun Fragment.setFragmentResultListener( 17 | requestKey: String, 18 | listener: ((requestKey: String, bundle: Bundle) -> Unit) 19 | ) { 20 | parentFragmentManager.setFragmentResultListener(requestKey, this, listener) 21 | } 22 | 23 | fun Fragment.collectLifecycleFlow(flow: Flow, collect: suspend (T) -> Unit) { 24 | viewLifecycleOwner.lifecycleScope.launch { 25 | viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { 26 | flow.collect(collect) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/JsonExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import com.google.gson.Gson 4 | 5 | 6 | fun T.toJson(): String { 7 | return Gson().toJson(this) 8 | } 9 | 10 | inline fun String.fromJson(): T { 11 | return Gson().fromJson(this, T::class.java) 12 | } 13 | 14 | inline fun fromJsonToList(jsonString: String): List { 15 | return Gson().fromJson(jsonString, emptyArray().javaClass).toList() 16 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/KeyboardExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.view.View 6 | import android.view.inputmethod.InputMethodManager 7 | 8 | 9 | /** 10 | * Use only from Activities, don't use from Fragment (with getActivity) or from Dialog/DialogFragment 11 | */ 12 | fun Activity.hideKeyboard() { 13 | val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager 14 | val view = currentFocus ?: View(this) 15 | imm.hideSoftInputFromWindow(view.windowToken, 0) 16 | window.decorView 17 | } 18 | 19 | fun View.showKeyboard() { 20 | val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 21 | imm.showSoftInput(this, InputMethodManager.SHOW_FORCED) 22 | } 23 | 24 | /** 25 | * Use everywhere except from Activity (Custom View, Fragment, Dialogs, DialogFragments). 26 | */ 27 | fun View.hideKeyboard() { 28 | val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 29 | imm.hideSoftInputFromWindow(windowToken, 0) 30 | } 31 | -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/NavigationExtension.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.annotation.IdRes 6 | import androidx.appcompat.widget.Toolbar 7 | import androidx.fragment.app.Fragment 8 | import androidx.navigation.* 9 | import androidx.navigation.fragment.findNavController 10 | 11 | fun View.navigate(direction: NavDirections) { 12 | catch { 13 | findNavController().navigate(direction) 14 | } 15 | } 16 | 17 | fun View.navigate(@IdRes destination: Int, args: Bundle? = null) { 18 | catch { 19 | findNavController() 20 | .navigate(destination, args) 21 | } 22 | } 23 | 24 | fun Fragment.navigate(@IdRes destination: Int, args: Bundle? = null) { 25 | catch { 26 | findNavController().navigate(destination, args) 27 | } 28 | } 29 | 30 | fun Fragment.navigate(direction: NavDirections, options: NavOptions? = null) { 31 | catch { 32 | findNavController().navigate(direction, options) 33 | } 34 | } 35 | 36 | fun Fragment.navigate(direction: NavDirections, extras: Navigator.Extras) { 37 | catch { 38 | findNavController().navigate(direction, extras) 39 | } 40 | } 41 | 42 | fun Fragment.back() { 43 | catch { 44 | findNavController().popBackStack() 45 | } 46 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/ThemeExtentions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import androidx.appcompat.app.AppCompatDelegate 4 | 5 | object ThemeHelper { 6 | fun applyTheme(theme: ThemeMode) { 7 | when (theme) { 8 | ThemeMode.LIGHT -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) 9 | ThemeMode.DARK -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) 10 | ThemeMode.BATTERY_SAVER -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY) 11 | ThemeMode.DEFAULT -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) 12 | } 13 | } 14 | 15 | enum class ThemeMode { LIGHT, DARK, BATTERY_SAVER, DEFAULT } 16 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/extensions/ToastExtenstions.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.extensions 2 | 3 | import android.content.Context 4 | import android.widget.Toast 5 | import com.devm7mdibrahim.utils.R 6 | import es.dmoral.toasty.Toasty 7 | 8 | fun Context.showToast(message: String?, toastType: ToastType = ToastType.ERROR, withIcon: Boolean = true) { 9 | if (message.isNullOrEmpty()) return 10 | when (toastType) { 11 | ToastType.SUCCESS -> { 12 | Toasty.success(this, message, Toast.LENGTH_SHORT, withIcon).show() 13 | } 14 | ToastType.ERROR -> { 15 | Toasty.error(this, message, Toast.LENGTH_SHORT, withIcon).show() 16 | } 17 | ToastType.INFO -> { 18 | Toasty.info(this, message, Toast.LENGTH_SHORT, withIcon).show() 19 | } 20 | ToastType.WARNING -> { 21 | Toasty.warning(this, message, Toast.LENGTH_SHORT, withIcon).show() 22 | } 23 | } 24 | } 25 | 26 | fun Context.toastNoInternetConnection() { 27 | Toasty.error( 28 | this, 29 | getString(R.string.no_internet_connection), 30 | Toast.LENGTH_SHORT, 31 | true 32 | ).show() 33 | } 34 | 35 | enum class ToastType { 36 | SUCCESS, ERROR, WARNING, INFO 37 | } 38 | -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/file/FileUtils.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.file 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.ContentResolver 5 | import android.content.Context 6 | import android.net.Uri 7 | import android.os.Environment 8 | import android.provider.OpenableColumns 9 | import kotlinx.coroutines.runBlocking 10 | import java.io.File 11 | import java.io.FileOutputStream 12 | import java.io.InputStream 13 | import java.io.OutputStream 14 | 15 | class FileUtils constructor(val context: Context) { 16 | 17 | fun saveFileInStorage(uri: Uri): String { 18 | var path = "" 19 | runBlocking { 20 | try { 21 | val file: File? 22 | val mimeType: String? = context.contentResolver.getType(uri) 23 | if (mimeType != null) { 24 | val inputStream: InputStream? = context.contentResolver.openInputStream(uri) 25 | val fileName = context.contentResolver.getFileName(uri) 26 | 27 | if (fileName != "") { 28 | file = File( 29 | context.getExternalFilesDir( 30 | Environment.DIRECTORY_DOWNLOADS 31 | )?.absolutePath.toString() + "/" + fileName 32 | ) 33 | val output: OutputStream = FileOutputStream(file) 34 | output.use { it -> 35 | val buffer = 36 | ByteArray(inputStream?.available()!!) 37 | var read: Int 38 | while (inputStream.read(buffer).also { read = it } != -1) { 39 | it.write(buffer, 0, read) 40 | } 41 | it.flush() 42 | path = file.absolutePath //use this path 43 | } 44 | } 45 | } 46 | } catch (e: Exception) { 47 | e.printStackTrace() 48 | } 49 | } 50 | return path 51 | } 52 | 53 | @SuppressLint("Range") 54 | fun ContentResolver.getFileName(uri: Uri): String { 55 | var mName = "" 56 | val cursor = query(uri, null, null, null, null) 57 | cursor?.use { 58 | it.moveToFirst() 59 | mName = cursor.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME)) 60 | } 61 | return mName 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/spinner/MaterialSpinnerUtil.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.spinner 2 | 3 | import android.content.Context 4 | import android.widget.AdapterView 5 | import android.widget.ArrayAdapter 6 | import android.widget.AutoCompleteTextView 7 | 8 | fun AutoCompleteTextView.init( 9 | context: Context, 10 | list: List, 11 | onItemSelected: (Int) -> Unit 12 | ) { 13 | val arrayAdapter = 14 | ArrayAdapter(context, android.R.layout.simple_list_item_1, list) 15 | setAdapter(arrayAdapter) 16 | onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> 17 | onItemSelected(position) 18 | } 19 | } -------------------------------------------------------------------------------- /utils/src/main/java/com/devm7mdibrahim/utils/spinner/SpinnerUtil.kt: -------------------------------------------------------------------------------- 1 | package com.devm7mdibrahim.utils.spinner 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.widget.AdapterView 6 | import android.widget.ArrayAdapter 7 | import android.widget.Spinner 8 | 9 | object SpinnerUtil { 10 | 11 | fun setSpinnerAdapter( 12 | context: Context, 13 | spinner: Spinner, 14 | list: List, 15 | onItemSelected: (Int) -> Unit 16 | ): ArrayAdapter { 17 | 18 | val arrayAdapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, list) 19 | arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) 20 | spinner.adapter = arrayAdapter 21 | 22 | spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { 23 | override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) { 24 | onItemSelected(position) 25 | } 26 | 27 | override fun onNothingSelected(p0: AdapterView<*>?) { 28 | 29 | } 30 | } 31 | 32 | return arrayAdapter 33 | } 34 | } -------------------------------------------------------------------------------- /utils/src/main/res/drawable/bg_top_rounded_white.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /utils/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /utils/src/main/res/layout/progress.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /utils/src/main/res/values-ar/strings.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 | هذا النص هو مثال لنص يمكن أن يستبدل في نفس المساحة، 30 | لقد تم توليد هذاالنص من مولد النص العربى، 31 | هذا النص هو مثال لنص يمكن أن يستبدل في نفس المساحة 32 | -------------------------------------------------------------------------------- /utils/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | #F8F8F8 5 | #3DB9B4 6 | #F8F8F8 7 | #FFFFFF 8 | #000000 9 | #FFB72B 10 | #D40C0C 11 | -------------------------------------------------------------------------------- /utils/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OK 4 | Cancel 5 | SAR 6 | No internet connection 7 | Alert 8 | Are you sure you want to logout? 9 | You must login to continue 10 | Your account has been blocked or deleted. In the event of an error, please contact the application administration. 11 | 12 | Timeout, please try again 13 | Something went wrong, please try again 14 | Something went wrong, please try again later 15 | Something went wrong, please try again later 16 | No internet connection 17 | UnAuthorized user 18 | 19 | Invalid phone number 20 | Invalid user name 21 | Invalid password 22 | Invalid confirmation password 23 | Invalid email address 24 | Invalid device id 25 | Please enter the verification code before confirm 26 | 27 | This text is an example of a text that can be replaced in the same space, 28 | this text was generated from the Arabic text generator, 29 | this text is an example of text that can be replaced in the same space, 30 | this text was generated from the Arabic text generator, 31 | this text is an example of a text that can be replaced To replace in the same space, 32 | it has been taken over 33 | --------------------------------------------------------------------------------