├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── dispatch │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── dispatch │ │ │ ├── app │ │ │ └── App.kt │ │ │ ├── di │ │ │ ├── DataModule.kt │ │ │ └── DomainModule.kt │ │ │ └── presentation │ │ │ ├── MainActivity.kt │ │ │ ├── currentUserProfile │ │ │ ├── CurrentUserProfileContract.kt │ │ │ ├── view │ │ │ │ ├── CurrentUserProfileFragment.kt │ │ │ │ └── ValidEditTextUserDetails.kt │ │ │ └── viewmodel │ │ │ │ └── CurrentUserProfileViewModel.kt │ │ │ ├── detailsMessages │ │ │ ├── DetailsMessagesContract.kt │ │ │ ├── view │ │ │ │ ├── DeleteMessagesDialog.kt │ │ │ │ ├── DetailsMessagesFragment.kt │ │ │ │ ├── MessageFromItem.kt │ │ │ │ └── MessageToItem.kt │ │ │ └── viewmodel │ │ │ │ └── DetailsMessagesViewModel.kt │ │ │ ├── latestMessages │ │ │ ├── LatestMessagesContract.kt │ │ │ ├── view │ │ │ │ ├── LatestMessageItem.kt │ │ │ │ └── LatestMessagesFragment.kt │ │ │ └── viewmodel │ │ │ │ └── LatestMessagesViewModel.kt │ │ │ ├── listUsers │ │ │ ├── ListUsersContract.kt │ │ │ ├── view │ │ │ │ ├── ListUsersFragment.kt │ │ │ │ └── UserItem.kt │ │ │ └── viewmodel │ │ │ │ └── ListUsersViewModel.kt │ │ │ ├── restorePassword │ │ │ ├── RestorePasswordContract.kt │ │ │ ├── view │ │ │ │ └── RestorePasswordFragment.kt │ │ │ └── viewmodel │ │ │ │ └── RestorePasswordViewModel.kt │ │ │ ├── signIn │ │ │ ├── SignInContract.kt │ │ │ ├── view │ │ │ │ └── SignInFragment.kt │ │ │ └── viewmodel │ │ │ │ └── SignInViewModel.kt │ │ │ ├── signUp │ │ │ ├── SignUpContract.kt │ │ │ ├── view │ │ │ │ └── SignUpFragment.kt │ │ │ └── viewmodel │ │ │ │ └── SignUpViewModel.kt │ │ │ └── splash │ │ │ ├── SplashScreenContract.kt │ │ │ ├── view │ │ │ └── SplashScreenFragment.kt │ │ │ └── viewmodel │ │ │ └── SplashScreenViewModel.kt │ └── res │ │ ├── drawable-v24 │ │ ├── ic_launcher_foreground.xml │ │ └── splash_screen.png │ │ ├── drawable │ │ ├── background_circle.xml │ │ ├── background_content_bottom.xml │ │ ├── background_content_top.xml │ │ ├── background_details_messages_input.xml │ │ ├── background_edittext.xml │ │ ├── background_recipient_message.xml │ │ ├── background_sender_message.xml │ │ ├── background_shapeableimageview.xml │ │ ├── background_view_circle.xml │ │ ├── ic_account_circle.xml │ │ ├── ic_arrow_back.xml │ │ ├── ic_calendar_month.xml │ │ ├── ic_cleaning_services.xml │ │ ├── ic_delete.xml │ │ ├── ic_info.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_lock.xml │ │ ├── ic_logout.xml │ │ ├── ic_mail.xml │ │ ├── ic_more_vert.xml │ │ ├── ic_person.xml │ │ ├── ic_remove_circle.xml │ │ ├── ic_send.xml │ │ ├── ic_translate.xml │ │ └── ic_two_user_circle.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── fragment_current_user_profile.xml │ │ ├── fragment_details_messages.xml │ │ ├── fragment_latest_messages.xml │ │ ├── fragment_list_users.xml │ │ ├── fragment_restore_password.xml │ │ ├── fragment_sign_in.xml │ │ ├── fragment_sign_up.xml │ │ ├── fragment_splash_screen.xml │ │ ├── item_container_latest_message.xml │ │ ├── item_container_recipient_message.xml │ │ ├── item_container_sender_message.xml │ │ └── item_container_user.xml │ │ ├── menu │ │ ├── bottom_menu.xml │ │ └── details_messages_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── navigation │ │ └── nav_graph.xml │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── example │ └── dispatch │ └── ExampleUnitTest.kt ├── build.gradle ├── data ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── dispatch │ │ └── data │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── example │ │ └── dispatch │ │ └── data │ │ ├── repository │ │ ├── MessageRepositoryImpl.kt │ │ ├── TranslateRepositoryImpl.kt │ │ ├── UserAuthRepositoryImpl.kt │ │ ├── UserDetailsRepositoryImpl.kt │ │ └── UserImagesRepositoryImpl.kt │ │ └── storage │ │ ├── MessageStorage.kt │ │ ├── TranslateStorage.kt │ │ ├── UserAuthStorage.kt │ │ ├── UserDetailsStorage.kt │ │ ├── UserImagesStorage.kt │ │ ├── firebase │ │ ├── FirebaseMessageStorage.kt │ │ ├── FirebaseUserAuthStorage.kt │ │ ├── FirebaseUserDetailsStorage.kt │ │ └── FirebaseUserImagesStorage.kt │ │ └── mlkit │ │ └── MlKitTranslateStorage.kt │ └── test │ └── java │ └── com │ └── example │ └── dispatch │ └── data │ └── ExampleUnitTest.kt ├── domain ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── example │ └── dispatch │ └── domain │ ├── constants │ └── LanguageCodeConstants.kt │ ├── models │ ├── FromToUser.kt │ ├── Message.kt │ ├── Response.kt │ ├── UserAuth.kt │ ├── UserDetails.kt │ └── UserDetailsPublic.kt │ ├── repository │ ├── MessageRepository.kt │ ├── TranslateRepository.kt │ ├── UserAuthRepository.kt │ ├── UserDetailsRepository.kt │ └── UserImagesRepository.kt │ └── usecase │ ├── ChangeUserAuthEmailUseCase.kt │ ├── ChangeUserAuthPasswordUseCase.kt │ ├── ChangeUserDetailsEmailUseCase.kt │ ├── ChangeUserDetailsFullnameUseCase.kt │ ├── ChangeUserDetailsPasswordUseCase.kt │ ├── ChangeUserDetailsPhotoProfileUrlUseCase.kt │ ├── CheckUserAuthSignedInUseCase.kt │ ├── DeleteCurrentUserAuthUseCase.kt │ ├── DeleteCurrentUserDetailsUseCase.kt │ ├── DeleteDialogBothUsersUseCase.kt │ ├── DeleteLatestMessagesBothUsersUseCase.kt │ ├── DeleteUserImageProfileUseCase.kt │ ├── DownloadLangRussianEnglishPackUseCase.kt │ ├── GetCurrentUserDetailsUseCase.kt │ ├── GetCurrentUserUidUseCase.kt │ ├── GetLatestMessagesUseCase.kt │ ├── GetUserDetailsPublicOnUidUseCase.kt │ ├── GetUsersListUseCase.kt │ ├── LanguageIdentifierUseCase.kt │ ├── ListenFromToUserMessagesUseCase.kt │ ├── RestoreUserByEmailUseCase.kt │ ├── SaveLatestMessageUseCase.kt │ ├── SaveMessageUseCase.kt │ ├── SaveUserDetailsUseCase.kt │ ├── SaveUserImageProfileUseCase.kt │ ├── SignInUserAuthUseCase.kt │ ├── SignOutUserAuthUseCase.kt │ ├── SignUpUserAuthUseCase.kt │ ├── TranslateEnglishRussianTextUseCase.kt │ └── TranslateRussianEnglishTextUseCase.kt ├── github_images ├── example1.png ├── example2.png ├── example3.png ├── example4.png ├── example5.png ├── example6.png └── example7.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 40 | 41 | 42 | 43 | 44 | 45 | 47 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Dispatch 2 | 3 | This project contains a demo messenger for communicating with each other without knowledge of English in English (through a translator), created using Kotlin and Firebase. 4 | 5 | The ideology of the project is to improve the level of English by communicating with each other, sending a message in Russian, it is translated into English and saved in both languages. 6 | It is possible to send a message in English (if you know how to write a sentence in English) - it will be translated into Russian and saved in both languages. 7 | Upon receipt, all messages are displayed in English. 8 | 9 | ## Get started 10 | 11 | To get started you need to first create a Firebase project for your app and add the google-service.json file in your project. 12 | After adding the google-services.json file, you need to create a database in Firebase Authentication, Firebase Database, Firebase Storage. 13 | Firebase Authentication needs to allow user registration by email. 14 | 15 | ## Screenshots 16 | 17 |

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |

26 | 27 | 28 | ## Demonstration application on youtube video 29 | 30 | You can see the application in action on youtube video hosting at the link: https://youtu.be/KlT0G5GxQU4 . 31 | 32 | ## Used tech 33 | 34 | * [Kotlin](https://kotlinlang.org/) 35 | * [Kotlin flows](https://developer.android.com/kotlin/flow) 36 | * [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) - Clean architecture in Android. 37 | * [MVVM](https://developer.android.com/jetpack/docs/guide) - Architectural pattern. 38 | * [Dagger Hilt](https://developer.android.com/training/dependency-injection/hilt-android) - Standard library to incorporate Dagger dependency injection into an Android application. 39 | * [Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) - Asynchronous programming. 40 | * [View Binding](https://developer.android.com/topic/libraries/data-binding/) - Declaratively bind observable data to UI elements. 41 | * [Lifecycles](https://developer.android.com/topic/libraries/architecture/lifecycle) - Create a UI that automatically responds to lifecycle events. 42 | * [LiveData](https://developer.android.com/topic/libraries/architecture/livedata) - Build data objects that notify views when the underlying database changes. 43 | * [Navigation](https://developer.android.com/guide/navigation/) - Handle everything needed for in-app navigation. 44 | * [Firebase](https://firebase.google.com/docs) - Tools to develop high-quality apps. 45 | * [SDP](https://github.com/intuit/sdp) - Scalable size unit. 46 | * [SSP](https://github.com/intuit/sdp) - Scalable size unit for text. 47 | * [Android Image Cropper](https://github.com/ArthurHub/Android-Image-Cropper) - Powerful (Zoom, Rotation, Multi-Source), customizable (Shape, Limits, Style), optimized (Async, Sampling, Matrix) and simple image cropping library for Android. 48 | * [Picasso](https://github.com/square/picasso) - Load and cache images by URL. 49 | * [Groupie](https://github.com/lisawray/groupie) - Groupie is a simple, flexible library for complex RecyclerView layouts. 50 | * [Ml-Kit Translator](https://developers.google.com/ml-kit/language/translation) - Ml-Kit translator API. 51 | * [Ml-kit Language](https://developers.google.com/ml-kit/language/identification/android) - Ml-Kit language id identificator for text. 52 | 53 | ## Find this repository useful? ❤ 54 | 55 | Support it by joining stargazers for this repository 🌟 56 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'com.google.gms.google-services' 5 | id 'kotlin-kapt' 6 | id 'dagger.hilt.android.plugin' 7 | } 8 | 9 | android { 10 | compileSdk 32 11 | 12 | defaultConfig { 13 | applicationId "com.example.dispatch" 14 | minSdk 21 15 | targetSdk 32 16 | versionCode 1 17 | versionName "1.0" 18 | 19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | buildFeatures { 29 | viewBinding true 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | } 39 | 40 | dependencies { 41 | 42 | implementation 'androidx.core:core-ktx:1.7.0' 43 | implementation 'androidx.appcompat:appcompat:1.4.1' 44 | implementation 'com.google.android.material:material:1.5.0' 45 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 46 | implementation "androidx.fragment:fragment-ktx:1.4.1" 47 | 48 | // navigation component 49 | implementation "androidx.navigation:navigation-fragment-ktx:2.4.2" 50 | implementation "androidx.navigation:navigation-ui-ktx:2.4.2" 51 | implementation "androidx.navigation:navigation-dynamic-features-fragment:2.4.2" 52 | 53 | // sdp and ssp / scalable size unit 54 | implementation 'com.intuit.sdp:sdp-android:1.0.6' 55 | implementation 'com.intuit.ssp:ssp-android:1.0.6' 56 | 57 | // dagger hilt 58 | implementation 'com.google.dagger:hilt-android:2.41' 59 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 60 | kapt 'com.google.dagger:hilt-compiler:2.41' 61 | 62 | // coroutines 63 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0' 64 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' 65 | 66 | // lifecycle 67 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1' 68 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1' 69 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1' 70 | 71 | // crop image view 72 | implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0' 73 | 74 | // picasso 75 | implementation 'com.squareup.picasso:picasso:2.71828' 76 | implementation 'jp.wasabeef:picasso-transformations:2.4.0' 77 | 78 | // groupie RecyclerView 79 | implementation 'com.github.lisawray.groupie:groupie:2.9.0' 80 | implementation 'com.github.lisawray.groupie:groupie-viewbinding:2.9.0' 81 | 82 | testImplementation 'junit:junit:4.13.2' 83 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 84 | 85 | implementation project(path: ':domain') 86 | implementation project(path: ':data') 87 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/dispatch/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.example.dispatch", appContext.packageName) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/app/App.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.app 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | 6 | @HiltAndroidApp 7 | class App : Application() 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/di/DataModule.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.di 2 | 3 | import com.example.dispatch.data.repository.* 4 | import com.example.dispatch.data.storage.* 5 | import com.example.dispatch.data.storage.firebase.FirebaseMessageStorage 6 | import com.example.dispatch.data.storage.firebase.FirebaseUserAuthStorage 7 | import com.example.dispatch.data.storage.firebase.FirebaseUserDetailsStorage 8 | import com.example.dispatch.data.storage.firebase.FirebaseUserImagesStorage 9 | import com.example.dispatch.data.storage.mlkit.MlKitTranslateStorage 10 | import com.example.dispatch.domain.repository.* 11 | import dagger.Module 12 | import dagger.Provides 13 | import dagger.hilt.InstallIn 14 | import dagger.hilt.components.SingletonComponent 15 | import kotlinx.coroutines.ExperimentalCoroutinesApi 16 | import javax.inject.Singleton 17 | 18 | @Module 19 | @ExperimentalCoroutinesApi 20 | @InstallIn(SingletonComponent::class) 21 | class DataModule { 22 | @Provides 23 | @Singleton 24 | fun providesUserAuthStorage(): UserAuthStorage { 25 | return FirebaseUserAuthStorage() 26 | } 27 | 28 | @Provides 29 | @Singleton 30 | fun providesUserAuthRepository(userAuthStorage: UserAuthStorage): UserAuthRepository { 31 | return UserAuthRepositoryImpl(userAuthStorage = userAuthStorage) 32 | } 33 | 34 | @Provides 35 | @Singleton 36 | fun providesUserDetailsStorage(): UserDetailsStorage { 37 | return FirebaseUserDetailsStorage() 38 | } 39 | 40 | @Provides 41 | @Singleton 42 | fun providesUserDetailsRepository(userDetailsStorage: UserDetailsStorage): UserDetailsRepository { 43 | return UserDetailsRepositoryImpl(userDetailsStorage = userDetailsStorage) 44 | } 45 | 46 | @Provides 47 | @Singleton 48 | fun providesUserImagesStorage(): UserImagesStorage { 49 | return FirebaseUserImagesStorage() 50 | } 51 | 52 | @Provides 53 | @Singleton 54 | fun providesUserImagesRepository(userImagesStorage: UserImagesStorage): UserImagesRepository { 55 | return UserImagesRepositoryImpl(userImagesStorage = userImagesStorage) 56 | } 57 | 58 | @Provides 59 | @Singleton 60 | fun providesTranslateStorage(): TranslateStorage { 61 | return MlKitTranslateStorage() 62 | } 63 | 64 | @Provides 65 | @Singleton 66 | fun providesTranslateRepository(translateStorage: TranslateStorage): TranslateRepository { 67 | return TranslateRepositoryImpl(translateStorage = translateStorage) 68 | } 69 | 70 | @Provides 71 | @Singleton 72 | fun providesMessageStorage(): MessageStorage { 73 | return FirebaseMessageStorage() 74 | } 75 | 76 | @Provides 77 | @Singleton 78 | fun providesMessageRepository(messageStorage: MessageStorage): MessageRepository { 79 | return MessageRepositoryImpl(messageStorage = messageStorage) 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.navigation.NavController 7 | import androidx.navigation.Navigation 8 | import androidx.navigation.ui.NavigationUI 9 | import com.example.dispatch.R 10 | import com.example.dispatch.databinding.ActivityMainBinding 11 | import dagger.hilt.android.AndroidEntryPoint 12 | 13 | @AndroidEntryPoint 14 | class MainActivity : AppCompatActivity() { 15 | private lateinit var binding: ActivityMainBinding 16 | private lateinit var navController: NavController 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | binding = ActivityMainBinding.inflate(layoutInflater) 21 | setContentView(binding.root) 22 | 23 | setupNav() 24 | } 25 | 26 | private fun setupNav() { 27 | navController = Navigation.findNavController(this, R.id.container_fragment) 28 | NavigationUI.setupWithNavController(binding.bottomNavigation, navController) 29 | 30 | navController.addOnDestinationChangedListener { _, destination, _ -> 31 | when (destination.id) { 32 | R.id.currentUserProfileFragment -> showBottomNav() 33 | R.id.latestMessagesFragment -> showBottomNav() 34 | else -> hideBottomNav() 35 | } 36 | } 37 | } 38 | 39 | private fun showBottomNav() { 40 | binding.bottomNavigation.visibility = View.VISIBLE 41 | 42 | } 43 | 44 | private fun hideBottomNav() { 45 | binding.bottomNavigation.visibility = View.GONE 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/currentUserProfile/view/ValidEditTextUserDetails.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.currentUserProfile.view 2 | 3 | class ValidEditTextUserDetails( 4 | var fullname: Boolean = false, 5 | var email: Boolean = false 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/currentUserProfile/viewmodel/CurrentUserProfileViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.currentUserProfile.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.liveData 7 | import com.example.dispatch.domain.models.Response 8 | import com.example.dispatch.domain.models.UserAuth 9 | import com.example.dispatch.domain.models.UserDetails 10 | import com.example.dispatch.domain.usecase.* 11 | import com.example.dispatch.presentation.currentUserProfile.CurrentUserProfileContract 12 | import dagger.hilt.android.lifecycle.HiltViewModel 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.ExperimentalCoroutinesApi 15 | import javax.inject.Inject 16 | 17 | @HiltViewModel 18 | @ExperimentalCoroutinesApi 19 | class CurrentUserProfileViewModel @Inject constructor( 20 | private val signOutUserAuthUseCase: SignOutUserAuthUseCase, 21 | private val getCurrentUserDetailsUseCase: GetCurrentUserDetailsUseCase, 22 | private val saveUserImageProfileUseCase: SaveUserImageProfileUseCase, 23 | private val changeUserDetailsPhotoProfileUrlUseCase: ChangeUserDetailsPhotoProfileUrlUseCase, 24 | private val changeUserAuthEmailUseCase: ChangeUserAuthEmailUseCase, 25 | private val changeUserAuthPasswordUseCase: ChangeUserAuthPasswordUseCase, 26 | private val changeUserDetailsEmailUseCase: ChangeUserDetailsEmailUseCase, 27 | private val changeUserDetailsPasswordUseCase: ChangeUserDetailsPasswordUseCase, 28 | private val changeUserDetailsFullnameUseCase: ChangeUserDetailsFullnameUseCase, 29 | ) : ViewModel(), CurrentUserProfileContract.CurrentUserProfileViewModel { 30 | 31 | private val _cropImageView = MutableLiveData("") 32 | val cropImageView: LiveData = _cropImageView 33 | 34 | val _userDetails = MutableLiveData() 35 | val userDetails: LiveData = _userDetails 36 | 37 | override fun signOutUserAuth(): LiveData> = liveData(Dispatchers.IO) { 38 | try { 39 | signOutUserAuthUseCase.execute().collect { emit(it) } 40 | } catch (e: Exception) { 41 | emit(Response.Fail(e = e)) 42 | } 43 | } 44 | 45 | override fun getCurrentUserDetails(): LiveData> = 46 | liveData(Dispatchers.IO) { 47 | try { 48 | getCurrentUserDetailsUseCase.execute().collect { emit(it) } 49 | } catch (e: Exception) { 50 | emit(Response.Fail(e = e)) 51 | } 52 | } 53 | 54 | override fun saveUserImageProfile(imageUriCache: String): LiveData> = 55 | liveData(Dispatchers.IO) { 56 | try { 57 | saveUserImageProfileUseCase.execute(newImageUriStr = imageUriCache) 58 | .collect { emit(it) } 59 | } catch (e: Exception) { 60 | emit(Response.Fail(e)) 61 | } 62 | } 63 | 64 | override fun changeUserDetailsPhotoProfileUrl(imageUriStr: String): LiveData> = 65 | liveData(Dispatchers.IO) { 66 | try { 67 | changeUserDetailsPhotoProfileUrlUseCase.execute(newImageUriStr = imageUriStr) 68 | .collect { emit(it) } 69 | } catch (e: Exception) { 70 | emit(Response.Fail(e)) 71 | } 72 | } 73 | 74 | override fun changeUserAuthEmail( 75 | userAuth: UserAuth, 76 | newEmail: String 77 | ): LiveData> = 78 | liveData(Dispatchers.IO) { 79 | try { 80 | changeUserAuthEmailUseCase.execute(userAuth = userAuth, newEmail = newEmail) 81 | .collect { emit(it) } 82 | } catch (e: Exception) { 83 | emit(Response.Fail(e = e)) 84 | } 85 | } 86 | 87 | override fun changeUserDetailsEmail(newEmail: String): LiveData> = 88 | liveData(Dispatchers.IO) { 89 | try { 90 | changeUserDetailsEmailUseCase.execute(newEmail = newEmail).collect { emit(it) } 91 | } catch (e: Exception) { 92 | emit(Response.Fail(e = e)) 93 | } 94 | } 95 | 96 | override fun changeUserAuthPassword( 97 | userAuth: UserAuth, 98 | newPassword: String 99 | ): LiveData> = 100 | liveData(Dispatchers.IO) { 101 | try { 102 | changeUserAuthPasswordUseCase.execute( 103 | userAuth = userAuth, 104 | newPassword = newPassword 105 | ) 106 | .collect { emit(it) } 107 | } catch (e: Exception) { 108 | emit(Response.Fail(e = e)) 109 | } 110 | } 111 | 112 | override fun changeUserDetailsPassword(newPassword: String): LiveData> = 113 | liveData(Dispatchers.IO) { 114 | try { 115 | changeUserDetailsPasswordUseCase.execute(newPassword = newPassword) 116 | .collect { emit(it) } 117 | } catch (e: Exception) { 118 | emit(Response.Fail(e = e)) 119 | } 120 | } 121 | 122 | override fun changeUserDetailsFullname(newFullname: String): LiveData> = 123 | liveData(Dispatchers.IO) { 124 | try { 125 | changeUserDetailsFullnameUseCase.execute(newFullname = newFullname) 126 | .collect { emit(it) } 127 | } catch (e: Exception) { 128 | emit(Response.Fail(e = e)) 129 | } 130 | } 131 | 132 | override fun saveUserImageLiveData(imageUriStr: String) { 133 | if (imageUriStr.isNotEmpty()) { 134 | _cropImageView.value = imageUriStr 135 | } 136 | } 137 | 138 | override fun deleteUserImageLiveData() { 139 | _cropImageView.value = "" 140 | } 141 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/detailsMessages/view/DeleteMessagesDialog.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.detailsMessages.view 2 | 3 | import android.app.Dialog 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AlertDialog 6 | import androidx.fragment.app.DialogFragment 7 | import com.example.dispatch.presentation.detailsMessages.DetailsMessagesContract 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi 9 | 10 | @ExperimentalCoroutinesApi 11 | class DeleteMessagesDialog(private val dialogClickListener: DetailsMessagesContract.DeleteMessagesDialogClickListener) : 12 | DialogFragment() { 13 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 14 | return activity?.let { 15 | val alertDialog = AlertDialog.Builder(it) 16 | .setTitle("Deleting messages") 17 | .setMessage( 18 | "Are you sure you want to clear your chat history?" + 19 | " Messages will be deleted from both users." 20 | ).setPositiveButton("YES") { _, _ -> 21 | dialogClickListener.onClickPositive() 22 | }.setNegativeButton("CANCEL") { dialog, _ -> 23 | dialog.cancel() 24 | } 25 | 26 | alertDialog.create() 27 | } ?: throw IllegalStateException("Activity is null!") 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/detailsMessages/view/MessageFromItem.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.detailsMessages.view 2 | 3 | import android.view.View 4 | import com.example.dispatch.R 5 | import com.example.dispatch.databinding.ItemContainerSenderMessageBinding 6 | import com.example.dispatch.domain.models.Message 7 | import com.xwray.groupie.viewbinding.BindableItem 8 | import java.sql.Date 9 | import java.sql.Timestamp 10 | import java.text.SimpleDateFormat 11 | 12 | class MessageFromItem(private val message: Message) : 13 | BindableItem() { 14 | override fun bind(viewBinding: ItemContainerSenderMessageBinding, position: Int) { 15 | viewBinding.textViewMessage.text = message.englishMessage 16 | 17 | val netDate = Date(Timestamp(message.timestamp).time) 18 | val date = SimpleDateFormat("dd/MM/yy hh:mm a").format(netDate) 19 | viewBinding.textViewDateTime.text = date 20 | 21 | var textEnglish: Boolean = true 22 | viewBinding.imageviewTranslated.setOnClickListener { 23 | if (textEnglish) { 24 | viewBinding.textViewMessage.text = message.russianMessage 25 | textEnglish = false 26 | } else { 27 | viewBinding.textViewMessage.text = message.englishMessage 28 | textEnglish = true 29 | } 30 | } 31 | } 32 | 33 | override fun getLayout(): Int { 34 | return R.layout.item_container_sender_message 35 | } 36 | 37 | override fun initializeViewBinding(view: View): ItemContainerSenderMessageBinding { 38 | return ItemContainerSenderMessageBinding.bind(view) 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/detailsMessages/view/MessageToItem.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.detailsMessages.view 2 | 3 | import android.view.View 4 | import com.example.dispatch.R 5 | import com.example.dispatch.databinding.ItemContainerRecipientMessageBinding 6 | import com.example.dispatch.domain.models.Message 7 | import com.xwray.groupie.viewbinding.BindableItem 8 | import java.sql.Date 9 | import java.sql.Timestamp 10 | import java.text.SimpleDateFormat 11 | 12 | class MessageToItem(private val message: Message) : 13 | BindableItem() { 14 | override fun bind(viewBinding: ItemContainerRecipientMessageBinding, position: Int) { 15 | viewBinding.textViewMessage.text = message.englishMessage 16 | 17 | val netDate = Date(Timestamp(message.timestamp).time) 18 | val date = SimpleDateFormat("dd/MM/yy hh:mm a").format(netDate) 19 | viewBinding.textViewDateTime.text = date 20 | 21 | var textEnglish: Boolean = true 22 | viewBinding.imageviewTranslated.setOnClickListener { 23 | if (textEnglish) { 24 | viewBinding.textViewMessage.text = message.russianMessage 25 | textEnglish = false 26 | } else { 27 | viewBinding.textViewMessage.text = message.englishMessage 28 | textEnglish = true 29 | } 30 | } 31 | } 32 | 33 | override fun getLayout(): Int { 34 | return R.layout.item_container_recipient_message 35 | } 36 | 37 | override fun initializeViewBinding(view: View): ItemContainerRecipientMessageBinding { 38 | return ItemContainerRecipientMessageBinding.bind(view) 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/detailsMessages/viewmodel/DetailsMessagesViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.detailsMessages.viewmodel 2 | 3 | import androidx.lifecycle.* 4 | import com.example.dispatch.domain.models.FromToUser 5 | import com.example.dispatch.domain.models.Message 6 | import com.example.dispatch.domain.models.Response 7 | import com.example.dispatch.domain.models.UserDetailsPublic 8 | import com.example.dispatch.domain.usecase.* 9 | import com.example.dispatch.presentation.detailsMessages.DetailsMessagesContract 10 | import dagger.hilt.android.lifecycle.HiltViewModel 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.ExperimentalCoroutinesApi 13 | import kotlinx.coroutines.launch 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | @ExperimentalCoroutinesApi 18 | class DetailsMessagesViewModel @Inject constructor( 19 | private val getUserDetailsPublicOnUidUseCase: GetUserDetailsPublicOnUidUseCase, 20 | private val translateRussianEnglishTextUseCase: TranslateRussianEnglishTextUseCase, 21 | private val translateEnglishRussianTextUseCase: TranslateEnglishRussianTextUseCase, 22 | private val languageIdentifierUseCase: LanguageIdentifierUseCase, 23 | private val getCurrentUserUidUseCase: GetCurrentUserUidUseCase, 24 | private val saveMessageUseCase: SaveMessageUseCase, 25 | private val listenFromToUserMessagesUseCase: ListenFromToUserMessagesUseCase, 26 | private val deleteDialogBothUsersUseCase: DeleteDialogBothUsersUseCase, 27 | private val saveLatestMessageUseCase: SaveLatestMessageUseCase, 28 | private val deleteLatestMessagesBothUsersUseCase: DeleteLatestMessagesBothUsersUseCase 29 | ) : ViewModel(), DetailsMessagesContract.DetailsMessagesViewModel { 30 | 31 | val _companionUid = MutableLiveData() 32 | val companionUid: LiveData = _companionUid 33 | 34 | val _companionDetails = MutableLiveData() 35 | val companionDetails: LiveData = _companionDetails 36 | 37 | private val _currUserUid = MutableLiveData() 38 | val currUserUid: LiveData = _currUserUid 39 | 40 | override fun getUserDetailsPublicOnUid(uid: String): LiveData> = 41 | liveData(Dispatchers.IO) { 42 | try { 43 | getUserDetailsPublicOnUidUseCase.execute(uid = uid).collect { emit(it) } 44 | } catch (e: Exception) { 45 | emit(Response.Fail(e = e)) 46 | } 47 | } 48 | 49 | override fun translateRussianEnglishText(text: String): LiveData> = 50 | liveData(Dispatchers.IO) { 51 | try { 52 | translateRussianEnglishTextUseCase.execute(text = text).collect { emit(it) } 53 | } catch (e: Exception) { 54 | emit(Response.Fail(e = e)) 55 | } 56 | } 57 | 58 | override fun translateEnglishRussianText(text: String): LiveData> = 59 | liveData(Dispatchers.IO) { 60 | try { 61 | translateEnglishRussianTextUseCase.execute(text = text).collect { emit(it) } 62 | } catch (e: Exception) { 63 | emit(Response.Fail(e = e)) 64 | } 65 | } 66 | 67 | override fun languageIdentifier(text: String): LiveData> = 68 | liveData(Dispatchers.IO) { 69 | try { 70 | languageIdentifierUseCase.execute(text = text).collect { emit(it) } 71 | } catch (e: Exception) { 72 | emit(Response.Fail(e = e)) 73 | } 74 | } 75 | 76 | override fun getCurrentUserUid() { 77 | viewModelScope.launch(Dispatchers.IO) { 78 | getCurrentUserUidUseCase.execute().collect { result -> 79 | when (result) { 80 | is Response.Loading -> {} 81 | is Response.Fail -> {} 82 | is Response.Success -> this@DetailsMessagesViewModel._currUserUid.postValue( 83 | result.data 84 | ) 85 | } 86 | } 87 | } 88 | } 89 | 90 | override fun saveMessage(message: Message): LiveData> = 91 | liveData(Dispatchers.IO) { 92 | try { 93 | saveMessageUseCase.execute(message = message).collect { emit(it) } 94 | } catch (e: Exception) { 95 | emit(Response.Fail(e = e)) 96 | } 97 | } 98 | 99 | override fun saveLatestMessage(message: Message) { 100 | viewModelScope.launch(Dispatchers.IO) { 101 | saveLatestMessageUseCase.execute(message = message).collect {} 102 | } 103 | } 104 | 105 | override fun listenFromToUserMessages(fromToUser: FromToUser): LiveData> = 106 | liveData(Dispatchers.IO) { 107 | try { 108 | listenFromToUserMessagesUseCase.execute(fromToUser = fromToUser) 109 | .collect { emit(it) } 110 | } catch (e: Exception) { 111 | emit(Response.Fail(e = e)) 112 | } 113 | } 114 | 115 | override fun deleteDialogBothUsers(fromToUser: FromToUser) { 116 | viewModelScope.launch(Dispatchers.IO) { 117 | deleteDialogBothUsersUseCase.execute(fromToUser = fromToUser).collect { result -> 118 | when (result) { 119 | is Response.Success -> deleteLatestMessageBothUsers(fromToUser = fromToUser) 120 | else -> {} 121 | } 122 | } 123 | } 124 | } 125 | 126 | override fun deleteLatestMessageBothUsers(fromToUser: FromToUser) { 127 | viewModelScope.launch(Dispatchers.IO) { 128 | deleteLatestMessagesBothUsersUseCase.execute(fromToUser = fromToUser).collect {} 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/latestMessages/LatestMessagesContract.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.latestMessages 2 | 3 | import com.example.dispatch.domain.models.Message 4 | import com.example.dispatch.domain.models.UserDetailsPublic 5 | import kotlinx.coroutines.flow.Flow 6 | 7 | interface LatestMessagesContract { 8 | interface LatestMessagesFragment { 9 | /** 10 | * Sets the required setOnClickListener on the views fragment 11 | */ 12 | fun setOnClickListeners() 13 | 14 | /** 15 | * Observer userDetails LiveData from [LatestMessagesViewModel] 16 | */ 17 | fun userDetailsObserver() 18 | 19 | /** 20 | * Observer progressBarLoadUserDetails LiveData from [LatestMessagesViewModel] 21 | */ 22 | fun progressBarLoadUserDetailsObserver() 23 | 24 | /** 25 | * Observer loadCurrentUserDetailsSuccess LiveData from [LatestMessagesViewModel] 26 | */ 27 | fun loadCurrentUserDetailsSuccessObserver() 28 | 29 | /** 30 | * Observer latestMessagesList LiveData from [LatestMessagesViewModel] 31 | */ 32 | fun latestMessagesListObserver() 33 | 34 | /** 35 | * Observer progressBarLoadLatestMessagesList LiveData from [LatestMessagesViewModel] 36 | */ 37 | fun progressBarLoadLatestMessagesListObserver() 38 | 39 | /** 40 | * Observer loadLatestMessagesList LiveData from [LatestMessagesViewModel] 41 | */ 42 | fun loadLatestMessagesListObserver() 43 | 44 | /** 45 | * Shows progress bar load user details 46 | */ 47 | fun showProgressBarLoadUserDetails() 48 | 49 | /** 50 | * Hides progress bar load user details 51 | */ 52 | fun hideProgressBarLoadUserDetails() 53 | 54 | /** 55 | * Shows progress bar load latest messages 56 | */ 57 | fun showProgressBarLoadLatestMessages() 58 | 59 | /** 60 | * Hides progress bar load latest messages 61 | */ 62 | fun hideProgressBarLoadLatestMessages() 63 | 64 | /** 65 | * Show toast Toast.LENGTH_LONG type 66 | * @param text - text, shown in toast 67 | */ 68 | fun showToastLengthLong(text: String) 69 | 70 | /** 71 | * Navigate to ListUsersFragment 72 | */ 73 | fun navigateToListUsersFragment() 74 | 75 | /** 76 | * Add user item into adapter, update adapter 77 | * @param message - [Message] model 78 | * @param user - [UserDetailsPublic] model 79 | */ 80 | fun adapterAddLatestMessage(message: Message, user: UserDetailsPublic) 81 | 82 | /** 83 | * Navigate to DetailsMessagesFragment, passing the uid of the user selected 84 | * in the adapter to the fragment 85 | * @param selectedUserUid - selected user (uid) from adapter 86 | */ 87 | fun navigateToDetailsMessagesFragmentTransferSelectedUser(selectedUserUid: String) 88 | } 89 | 90 | interface LatestMessagesViewModel { 91 | /** 92 | * Getting the details of the currently logged in user 93 | */ 94 | fun getCurrentUserDetails() 95 | 96 | /** 97 | * Get ArrayList all latest messages current user 98 | * @param currentUserUid - [String] uid current user 99 | */ 100 | fun getLatestMessages(currentUserUid: String) 101 | 102 | /** 103 | * Get user details by his uid 104 | * @param uid - [String] uid user 105 | */ 106 | fun getUserDetailsPublicOnUid(uid: String): Flow 107 | 108 | /** 109 | * Clear latestMessagesList LiveData from [LatestMessagesViewModel] 110 | */ 111 | fun latestMessagesListClear() 112 | } 113 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/latestMessages/view/LatestMessageItem.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.latestMessages.view 2 | 3 | import android.view.View 4 | import com.example.dispatch.R 5 | import com.example.dispatch.databinding.ItemContainerLatestMessageBinding 6 | import com.example.dispatch.domain.models.Message 7 | import com.example.dispatch.domain.models.UserDetailsPublic 8 | import com.squareup.picasso.Picasso 9 | import com.xwray.groupie.viewbinding.BindableItem 10 | import jp.wasabeef.picasso.transformations.CropCircleTransformation 11 | 12 | class LatestMessageItem( 13 | val message: Message, 14 | val companionUser: UserDetailsPublic 15 | ) : BindableItem() { 16 | override fun bind(viewBinding: ItemContainerLatestMessageBinding, position: Int) { 17 | viewBinding.latestMessage.text = message.englishMessage 18 | viewBinding.textViewUserName.text = companionUser.fullname 19 | if (companionUser.photoProfileUrl.isNotEmpty()) { 20 | Picasso.get().load(companionUser.photoProfileUrl).transform(CropCircleTransformation()) 21 | .into(viewBinding.imageViewUserPhoto) 22 | } 23 | } 24 | 25 | override fun getLayout(): Int { 26 | return R.layout.item_container_latest_message 27 | } 28 | 29 | override fun initializeViewBinding(view: View): ItemContainerLatestMessageBinding { 30 | return ItemContainerLatestMessageBinding.bind(view) 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/latestMessages/viewmodel/LatestMessagesViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.latestMessages.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import com.example.dispatch.domain.models.Message 8 | import com.example.dispatch.domain.models.Response 9 | import com.example.dispatch.domain.models.UserDetails 10 | import com.example.dispatch.domain.models.UserDetailsPublic 11 | import com.example.dispatch.domain.usecase.GetCurrentUserDetailsUseCase 12 | import com.example.dispatch.domain.usecase.GetCurrentUserUidUseCase 13 | import com.example.dispatch.domain.usecase.GetLatestMessagesUseCase 14 | import com.example.dispatch.domain.usecase.GetUserDetailsPublicOnUidUseCase 15 | import com.example.dispatch.presentation.latestMessages.LatestMessagesContract 16 | import dagger.hilt.android.lifecycle.HiltViewModel 17 | import kotlinx.coroutines.Dispatchers 18 | import kotlinx.coroutines.ExperimentalCoroutinesApi 19 | import kotlinx.coroutines.flow.Flow 20 | import kotlinx.coroutines.flow.flow 21 | import kotlinx.coroutines.launch 22 | import javax.inject.Inject 23 | 24 | @HiltViewModel 25 | @ExperimentalCoroutinesApi 26 | class LatestMessagesViewModel @Inject constructor( 27 | private val getCurrentUserDetailsUseCase: GetCurrentUserDetailsUseCase, 28 | private val getLatestMessagesUseCase: GetLatestMessagesUseCase, 29 | private val getUserDetailsPublicOnUidUseCase: GetUserDetailsPublicOnUidUseCase, 30 | private val getCurrentUserUidUseCase: GetCurrentUserUidUseCase 31 | ) : ViewModel(), LatestMessagesContract.LatestMessagesViewModel { 32 | 33 | private val _userDetails = MutableLiveData(UserDetails()) 34 | val userDetails: LiveData = _userDetails 35 | 36 | private val _progressBarLoadUserDetails = MutableLiveData() 37 | val progressBarLoadUserDetails: LiveData = _progressBarLoadUserDetails 38 | 39 | private val _loadCurrentUserDetailsSuccess = MutableLiveData>() 40 | val loadCurrentUserDetailsSuccess: LiveData> = _loadCurrentUserDetailsSuccess 41 | 42 | private val _latestMessagesList = MutableLiveData>() 43 | val latestMessagesList: LiveData> = _latestMessagesList 44 | 45 | private val _progressBarLoadLatestMessagesList = MutableLiveData() 46 | val progressBarLoadLatestMessagesList: LiveData = _progressBarLoadLatestMessagesList 47 | 48 | private val _loadLatestMessagesList = MutableLiveData>() 49 | val loadLatestMessagesList: LiveData> = _loadLatestMessagesList 50 | 51 | private val _currentUserUid = MutableLiveData() 52 | val currentUserUid: LiveData = _currentUserUid 53 | 54 | override fun getCurrentUserDetails() { 55 | viewModelScope.launch(Dispatchers.IO) { 56 | getCurrentUserDetailsUseCase.execute().collect { result -> 57 | when (result) { 58 | is Response.Loading -> _progressBarLoadUserDetails.postValue(true) 59 | is Response.Fail -> { 60 | _progressBarLoadUserDetails.postValue(false) 61 | _loadCurrentUserDetailsSuccess.postValue(Response.Fail(e = result.e)) 62 | } 63 | is Response.Success -> { 64 | _progressBarLoadUserDetails.postValue(false) 65 | _loadCurrentUserDetailsSuccess.postValue(Response.Success(data = true)) 66 | this@LatestMessagesViewModel._userDetails.postValue(result.data) 67 | getLatestMessages(currentUserUid = result.data.uid) 68 | } 69 | } 70 | } 71 | } 72 | } 73 | 74 | override fun getLatestMessages(currentUserUid: String) { 75 | viewModelScope.launch(Dispatchers.IO) { 76 | getLatestMessagesUseCase.execute(fromUserUid = currentUserUid).collect { result -> 77 | when (result) { 78 | is Response.Loading -> _progressBarLoadLatestMessagesList.postValue(true) 79 | is Response.Fail -> { 80 | _progressBarLoadLatestMessagesList.postValue(false) 81 | _loadLatestMessagesList.postValue(Response.Fail(e = result.e)) 82 | } 83 | is Response.Success -> { 84 | _progressBarLoadLatestMessagesList.postValue(false) 85 | _loadLatestMessagesList.postValue(Response.Success(data = true)) 86 | this@LatestMessagesViewModel._latestMessagesList.postValue(result.data) 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | override fun getUserDetailsPublicOnUid(uid: String): Flow = flow { 94 | getUserDetailsPublicOnUidUseCase.execute(uid = uid).collect { result -> 95 | when (result) { 96 | is Response.Success -> emit(result.data) 97 | else -> {} 98 | } 99 | } 100 | } 101 | 102 | override fun latestMessagesListClear() { 103 | _latestMessagesList.value?.clear() 104 | } 105 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/listUsers/ListUsersContract.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.listUsers 2 | 3 | import com.example.dispatch.domain.models.UserDetailsPublic 4 | 5 | interface ListUsersContract { 6 | interface ListUsersFragment { 7 | /** 8 | * Sets the required setOnClickListener on the views fragment 9 | */ 10 | fun setOnClickListeners() 11 | 12 | /** 13 | * Observer usersList LiveData from [ListUsersViewModel] 14 | */ 15 | fun usersListObserver() 16 | 17 | /** 18 | * Observer progressBarListUsers LiveData from [ListUsersViewModel] 19 | */ 20 | fun progressBarListUsersObserver() 21 | 22 | /** 23 | * Shows progress bar list users 24 | */ 25 | fun showProgressBarListUsers() 26 | 27 | /** 28 | * Hides progress bar list users 29 | */ 30 | fun hideProgressBarListUsers() 31 | 32 | /** 33 | * Add user item into adapter, update adapter 34 | * @param userDetailsPublic - [UserDetailsPublic] model user 35 | */ 36 | fun adapterAddItemUser(userDetailsPublic: UserDetailsPublic) 37 | 38 | /** 39 | * Navigate to pop back stack 40 | */ 41 | fun navigateToPopBackStack() 42 | 43 | /** 44 | * Navigate to DetailsMessagesFragment, passing the uid of the user selected 45 | * in the adapter to the fragment 46 | * @param selectedUserUid - selected user (uid) from adapter 47 | */ 48 | fun navigateToDetailsMessagesFragmentTransferSelectedUser(selectedUserUid: String) 49 | } 50 | 51 | interface ListUsersViewModel { 52 | /** 53 | * Get list users 54 | */ 55 | fun getUsersList() 56 | 57 | /** 58 | * Get current user uid 59 | */ 60 | fun getCurrentUserUid() 61 | 62 | /** 63 | * Clear usersList LiveData 64 | */ 65 | fun usersListClear() 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/listUsers/view/ListUsersFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.listUsers.view 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.Fragment 8 | import androidx.fragment.app.viewModels 9 | import androidx.navigation.fragment.findNavController 10 | import com.example.dispatch.R 11 | import com.example.dispatch.databinding.FragmentListUsersBinding 12 | import com.example.dispatch.databinding.ItemContainerUserBinding 13 | import com.example.dispatch.domain.models.UserDetailsPublic 14 | import com.example.dispatch.presentation.detailsMessages.view.DetailsMessagesFragment 15 | import com.example.dispatch.presentation.listUsers.ListUsersContract 16 | import com.example.dispatch.presentation.listUsers.viewmodel.ListUsersViewModel 17 | import com.xwray.groupie.GroupAdapter 18 | import com.xwray.groupie.viewbinding.GroupieViewHolder 19 | import dagger.hilt.android.AndroidEntryPoint 20 | import kotlinx.coroutines.ExperimentalCoroutinesApi 21 | 22 | 23 | @AndroidEntryPoint 24 | @ExperimentalCoroutinesApi 25 | class ListUsersFragment : Fragment(), ListUsersContract.ListUsersFragment { 26 | 27 | private lateinit var binding: FragmentListUsersBinding 28 | private val viewModel: ListUsersViewModel by viewModels() 29 | private val adapter = GroupAdapter>() 30 | 31 | override fun onCreateView( 32 | inflater: LayoutInflater, container: ViewGroup?, 33 | savedInstanceState: Bundle? 34 | ): View { 35 | binding = FragmentListUsersBinding.inflate(inflater, container, false) 36 | return binding.root 37 | } 38 | 39 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 40 | super.onViewCreated(view, savedInstanceState) 41 | 42 | setOnClickListeners() 43 | progressBarListUsersObserver() 44 | usersListObserver() 45 | } 46 | 47 | override fun onStart() { 48 | super.onStart() 49 | viewModel.usersListClear() 50 | adapter.clear() 51 | viewModel.getCurrentUserUid() 52 | viewModel.getUsersList() 53 | } 54 | 55 | override fun setOnClickListeners() { 56 | binding.imageViewBack.setOnClickListener { 57 | navigateToPopBackStack() 58 | } 59 | 60 | adapter.setOnItemClickListener { item, _ -> 61 | val userItem = item as UserItem 62 | val selectedUserUid = userItem.user.uid 63 | 64 | navigateToDetailsMessagesFragmentTransferSelectedUser(selectedUserUid = selectedUserUid) 65 | } 66 | } 67 | 68 | override fun usersListObserver() { 69 | viewModel.usersList.observe(viewLifecycleOwner) { usersList -> 70 | usersList.forEach { userDetailsPublic -> 71 | if (userDetailsPublic.uid != viewModel.currentUserUid.value) { 72 | adapterAddItemUser(userDetailsPublic = userDetailsPublic) 73 | } 74 | } 75 | } 76 | } 77 | 78 | override fun progressBarListUsersObserver() { 79 | viewModel.progressBarListUsers.observe(viewLifecycleOwner) { result -> 80 | if (result) showProgressBarListUsers() 81 | else hideProgressBarListUsers() 82 | } 83 | } 84 | 85 | override fun showProgressBarListUsers() { 86 | binding.progressBarListUsers.visibility = View.VISIBLE 87 | } 88 | 89 | override fun hideProgressBarListUsers() { 90 | binding.progressBarListUsers.visibility = View.INVISIBLE 91 | } 92 | 93 | override fun adapterAddItemUser(userDetailsPublic: UserDetailsPublic) { 94 | adapter.add(UserItem(user = userDetailsPublic)) 95 | binding.recyclerViewListUsers.adapter = adapter 96 | } 97 | 98 | override fun navigateToPopBackStack() { 99 | findNavController().popBackStack() 100 | } 101 | 102 | override fun navigateToDetailsMessagesFragmentTransferSelectedUser(selectedUserUid: String) { 103 | findNavController().navigate( 104 | R.id.action_listUsersFragment_to_detailsMessagesFragment, 105 | Bundle().apply { 106 | putString(DetailsMessagesFragment.SELECTED_USER_UID, selectedUserUid) 107 | }) 108 | } 109 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/listUsers/view/UserItem.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.listUsers.view 2 | 3 | import android.view.View 4 | import com.example.dispatch.R 5 | import com.example.dispatch.databinding.ItemContainerUserBinding 6 | import com.example.dispatch.domain.models.UserDetailsPublic 7 | import com.squareup.picasso.Picasso 8 | import com.xwray.groupie.viewbinding.BindableItem 9 | import jp.wasabeef.picasso.transformations.CropCircleTransformation 10 | 11 | class UserItem(val user: UserDetailsPublic) : BindableItem() { 12 | override fun initializeViewBinding(view: View): ItemContainerUserBinding { 13 | return ItemContainerUserBinding.bind(view) 14 | } 15 | 16 | override fun bind(viewBinding: ItemContainerUserBinding, position: Int) { 17 | viewBinding.textViewFullname.text = user.fullname 18 | viewBinding.textViewEmail.text = user.email 19 | 20 | if (user.photoProfileUrl.isNotEmpty()) { 21 | Picasso.get().load(user.photoProfileUrl).transform(CropCircleTransformation()) 22 | .into(viewBinding.shapeableImageViewProfileImage) 23 | } 24 | } 25 | 26 | override fun getLayout(): Int { 27 | return R.layout.item_container_user 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/listUsers/viewmodel/ListUsersViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.listUsers.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import com.example.dispatch.domain.models.Response 8 | import com.example.dispatch.domain.models.UserDetailsPublic 9 | import com.example.dispatch.domain.usecase.GetCurrentUserUidUseCase 10 | import com.example.dispatch.domain.usecase.GetUsersListUseCase 11 | import com.example.dispatch.presentation.listUsers.ListUsersContract 12 | import dagger.hilt.android.lifecycle.HiltViewModel 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.ExperimentalCoroutinesApi 15 | import kotlinx.coroutines.launch 16 | import javax.inject.Inject 17 | 18 | @HiltViewModel 19 | @ExperimentalCoroutinesApi 20 | class ListUsersViewModel @Inject constructor( 21 | private val getUsersListUseCase: GetUsersListUseCase, 22 | private val getCurrentUserUidUseCase: GetCurrentUserUidUseCase 23 | ) : ViewModel(), ListUsersContract.ListUsersViewModel { 24 | 25 | private val _usersList = MutableLiveData>() 26 | val usersList: LiveData> = _usersList 27 | 28 | private val _currentUserUid = MutableLiveData() 29 | val currentUserUid: LiveData = _currentUserUid 30 | 31 | private val _progressBarListUsers = MutableLiveData() 32 | val progressBarListUsers: LiveData = _progressBarListUsers 33 | 34 | override fun getUsersList() { 35 | viewModelScope.launch(Dispatchers.IO) { 36 | getUsersListUseCase.execute().collect { result -> 37 | when (result) { 38 | is Response.Loading -> _progressBarListUsers.postValue(true) 39 | is Response.Fail -> _progressBarListUsers.postValue(false) 40 | is Response.Success -> { 41 | _progressBarListUsers.postValue(false) 42 | this@ListUsersViewModel._usersList.postValue(result.data) 43 | } 44 | } 45 | } 46 | } 47 | } 48 | 49 | override fun getCurrentUserUid() { 50 | viewModelScope.launch(Dispatchers.IO) { 51 | getCurrentUserUidUseCase.execute().collect { result -> 52 | when (result) { 53 | is Response.Success -> this@ListUsersViewModel._currentUserUid.postValue(result.data) 54 | else -> {} 55 | } 56 | } 57 | } 58 | } 59 | 60 | override fun usersListClear() { 61 | _usersList.value?.clear() 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/restorePassword/RestorePasswordContract.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.restorePassword 2 | 3 | interface RestorePasswordContract { 4 | interface RestorePasswordFragment { 5 | /** 6 | * Sets the required setOnClickListener on the views fragment 7 | */ 8 | fun setOnClickListeners() 9 | 10 | /** 11 | * Observer progressBarRestore LiveData from [RestorePasswordViewModel] 12 | */ 13 | fun progressBarRestoreObserver() 14 | 15 | /** 16 | * Observer restoreSuccess LiveData from [RestorePasswordViewModel] 17 | */ 18 | fun restoreSuccessObserver() 19 | 20 | /** 21 | * Initializes the email value with a string from the edittext 22 | * @return - initializes [String] email 23 | */ 24 | fun editTextEmailInit(): String 25 | 26 | /** 27 | * Checks the edittext for valid data 28 | * @return - corresponding boolean value (correct / incorrect) 29 | */ 30 | fun validEditTextShowError(): Boolean 31 | 32 | /** 33 | * Show toast Toast.LENGTH_LONG type 34 | * @param text - text, shown in toast 35 | */ 36 | fun showToastLengthLong(text: String) 37 | 38 | /** 39 | * Shows progress bar restore 40 | */ 41 | fun showProgressBarRestore() 42 | 43 | /** 44 | * Hides progress bar restore 45 | */ 46 | fun hideProgressBarRestore() 47 | 48 | /** 49 | * Navigate to pop back stack 50 | */ 51 | fun navigateToPopBackStack() 52 | } 53 | 54 | interface RestorePasswordViewModel { 55 | /** 56 | * Sends an email with instructions for account recovery 57 | * @param email - email associated with the account 58 | */ 59 | fun restoreUserByEmail(email: String) 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/restorePassword/view/RestorePasswordFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.restorePassword.view 2 | 3 | import android.os.Bundle 4 | import android.util.Patterns 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Toast 9 | import androidx.fragment.app.Fragment 10 | import androidx.fragment.app.viewModels 11 | import androidx.navigation.fragment.findNavController 12 | import com.example.dispatch.databinding.FragmentRestorePasswordBinding 13 | import com.example.dispatch.domain.models.Response 14 | import com.example.dispatch.presentation.restorePassword.RestorePasswordContract 15 | import com.example.dispatch.presentation.restorePassword.viewmodel.RestorePasswordViewModel 16 | import dagger.hilt.android.AndroidEntryPoint 17 | import kotlinx.coroutines.ExperimentalCoroutinesApi 18 | 19 | @AndroidEntryPoint 20 | @ExperimentalCoroutinesApi 21 | class RestorePasswordFragment : Fragment(), RestorePasswordContract.RestorePasswordFragment { 22 | private lateinit var binding: FragmentRestorePasswordBinding 23 | private val viewModel: RestorePasswordViewModel by viewModels() 24 | 25 | override fun onCreateView( 26 | inflater: LayoutInflater, container: ViewGroup?, 27 | savedInstanceState: Bundle? 28 | ): View { 29 | binding = FragmentRestorePasswordBinding.inflate(inflater, container, false) 30 | return binding.root 31 | } 32 | 33 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 34 | super.onViewCreated(view, savedInstanceState) 35 | 36 | setOnClickListeners() 37 | progressBarRestoreObserver() 38 | restoreSuccessObserver() 39 | } 40 | 41 | override fun setOnClickListeners() { 42 | binding.buttonBack.setOnClickListener { 43 | navigateToPopBackStack() 44 | } 45 | 46 | binding.buttonRestore.setOnClickListener { 47 | if (validEditTextShowError()) { 48 | val email = editTextEmailInit() 49 | viewModel.restoreUserByEmail(email = email) 50 | } 51 | } 52 | } 53 | 54 | override fun progressBarRestoreObserver() { 55 | viewModel.progressBarRestore.observe(viewLifecycleOwner) { result -> 56 | if (result) showProgressBarRestore() 57 | else hideProgressBarRestore() 58 | } 59 | } 60 | 61 | override fun restoreSuccessObserver() { 62 | viewModel.restoreSuccess.observe(viewLifecycleOwner) { result -> 63 | when (result) { 64 | is Response.Loading -> {} 65 | is Response.Fail -> showToastLengthLong(text = "Restore password failed: ${result.e}") 66 | is Response.Success -> { 67 | showToastLengthLong(text = "Check your email! ;)") 68 | navigateToPopBackStack() 69 | } 70 | } 71 | } 72 | } 73 | 74 | override fun editTextEmailInit(): String { 75 | return binding.edittextEmail.text.toString() 76 | } 77 | 78 | override fun validEditTextShowError(): Boolean { 79 | val email = binding.edittextEmail.text.toString() 80 | 81 | var valid = false 82 | 83 | when { 84 | email.isEmpty() -> { 85 | binding.edittextEmail.setError("Enter email address.", null) 86 | binding.edittextEmail.requestFocus() 87 | } 88 | !Patterns.EMAIL_ADDRESS.matcher(email).matches() -> { 89 | binding.edittextEmail.setError("Enter valid email address.", null) 90 | binding.edittextEmail.requestFocus() 91 | } 92 | else -> valid = true 93 | } 94 | 95 | return valid 96 | } 97 | 98 | override fun showToastLengthLong(text: String) { 99 | Toast.makeText(activity, text, Toast.LENGTH_LONG) 100 | .show() 101 | } 102 | 103 | override fun showProgressBarRestore() { 104 | binding.progressbarRestore.visibility = View.VISIBLE 105 | binding.buttonRestore.visibility = View.INVISIBLE 106 | } 107 | 108 | override fun hideProgressBarRestore() { 109 | binding.progressbarRestore.visibility = View.INVISIBLE 110 | binding.buttonRestore.visibility = View.VISIBLE 111 | } 112 | 113 | override fun navigateToPopBackStack() { 114 | findNavController().popBackStack() 115 | } 116 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/restorePassword/viewmodel/RestorePasswordViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.restorePassword.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import com.example.dispatch.domain.models.Response 8 | import com.example.dispatch.domain.usecase.RestoreUserByEmailUseCase 9 | import com.example.dispatch.presentation.restorePassword.RestorePasswordContract 10 | import dagger.hilt.android.lifecycle.HiltViewModel 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.ExperimentalCoroutinesApi 13 | import kotlinx.coroutines.launch 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | @ExperimentalCoroutinesApi 18 | class RestorePasswordViewModel @Inject constructor( 19 | private val restoreUserByEmailUseCase: RestoreUserByEmailUseCase 20 | ) : ViewModel(), RestorePasswordContract.RestorePasswordViewModel { 21 | 22 | private val _progressBarRestore = MutableLiveData() 23 | val progressBarRestore: LiveData = _progressBarRestore 24 | 25 | private val _restoreSuccess = MutableLiveData>() 26 | val restoreSuccess: LiveData> = _restoreSuccess 27 | 28 | override fun restoreUserByEmail(email: String) { 29 | viewModelScope.launch(Dispatchers.IO) { 30 | restoreUserByEmailUseCase.execute(email = email).collect { result -> 31 | when (result) { 32 | is Response.Loading -> _progressBarRestore.postValue(true) 33 | is Response.Fail -> { 34 | _progressBarRestore.postValue(false) 35 | _restoreSuccess.postValue(Response.Fail(e = result.e)) 36 | } 37 | is Response.Success -> { 38 | _progressBarRestore.postValue(false) 39 | _restoreSuccess.postValue(Response.Success(data = true)) 40 | } 41 | } 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/signIn/SignInContract.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.signIn 2 | 3 | import com.example.dispatch.domain.models.UserAuth 4 | import com.example.dispatch.presentation.latestMessages.LatestMessagesContract.LatestMessagesViewModel 5 | 6 | interface SignInContract { 7 | interface SignInFragment { 8 | /** 9 | * Sets the required setOnClickListener on the views fragment 10 | */ 11 | fun setOnClickListeners() 12 | 13 | /** 14 | * Initialize the UserAuth variable with values from the corresponding edittext 15 | * @return - initialized [UserAuth] 16 | */ 17 | fun editTextUserAuthInit(): UserAuth 18 | 19 | /** 20 | * Checks the edittext for valid data 21 | * @return - corresponding boolean value (correct / incorrect) 22 | */ 23 | fun validEditTextShowError(): Boolean 24 | 25 | /** 26 | * Observer progressBarSignIn LiveData from [SignInViewModel] 27 | */ 28 | fun progressBarSignInObserver() 29 | 30 | /** 31 | * Observer signInSuccess LiveData from [SignInViewModel] 32 | */ 33 | fun signInSuccessObserver() 34 | 35 | /** 36 | * Observer loadRussianEnglishPack LiveData from [LatestMessagesViewModel] 37 | */ 38 | fun loadRussianEnglishPackObserver() 39 | 40 | /** 41 | * Show toast Toast.LENGTH_LONG type 42 | * @param text - text, shown in toast 43 | */ 44 | fun showToastLengthLong(text: String) 45 | 46 | /** 47 | * Shows progress bar sign in 48 | */ 49 | fun showProgressBarSignIn() 50 | 51 | /** 52 | * Hides progress bar sign in 53 | */ 54 | fun hideProgressBarSignIn() 55 | 56 | /** 57 | * Navigate to SignUpFragment 58 | */ 59 | fun navigateToSignUpFragment() 60 | 61 | /** 62 | * Navigate to RestorePasswordFragment 63 | */ 64 | fun navigateToRestorePasswordFragment() 65 | 66 | /** 67 | * Navigate to LatestMessagesFragment 68 | */ 69 | fun navigateToLatestMessagesFragment() 70 | } 71 | 72 | interface SignInViewModel { 73 | /** 74 | * User authorization in the system 75 | * @param userAuth - authorization data 76 | */ 77 | fun signInUserAuth(userAuth: UserAuth) 78 | 79 | /** 80 | * Download language ru-en pack (ml kit translate) 81 | */ 82 | fun downloadLangRussianEnglishPack() 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/signIn/view/SignInFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.signIn.view 2 | 3 | import android.os.Bundle 4 | import android.util.Patterns 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Toast 9 | import androidx.fragment.app.Fragment 10 | import androidx.fragment.app.viewModels 11 | import androidx.navigation.fragment.findNavController 12 | import com.example.dispatch.R 13 | import com.example.dispatch.databinding.FragmentSignInBinding 14 | import com.example.dispatch.domain.models.Response 15 | import com.example.dispatch.domain.models.UserAuth 16 | import com.example.dispatch.presentation.signIn.SignInContract 17 | import com.example.dispatch.presentation.signIn.viewmodel.SignInViewModel 18 | import dagger.hilt.android.AndroidEntryPoint 19 | import kotlinx.coroutines.ExperimentalCoroutinesApi 20 | 21 | @AndroidEntryPoint 22 | @ExperimentalCoroutinesApi 23 | class SignInFragment : Fragment(), SignInContract.SignInFragment { 24 | private lateinit var binding: FragmentSignInBinding 25 | private val viewModel: SignInViewModel by viewModels() 26 | 27 | override fun onCreateView( 28 | inflater: LayoutInflater, container: ViewGroup?, 29 | savedInstanceState: Bundle? 30 | ): View { 31 | binding = FragmentSignInBinding.inflate(inflater, container, false) 32 | return binding.root 33 | } 34 | 35 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 36 | super.onViewCreated(view, savedInstanceState) 37 | 38 | viewModel.downloadLangRussianEnglishPack() 39 | 40 | setOnClickListeners() 41 | progressBarSignInObserver() 42 | signInSuccessObserver() 43 | loadRussianEnglishPackObserver() 44 | } 45 | 46 | override fun setOnClickListeners() { 47 | binding.buttonSignUp.setOnClickListener { 48 | navigateToSignUpFragment() 49 | } 50 | 51 | binding.buttonSignIn.setOnClickListener { 52 | if (validEditTextShowError()) { 53 | val userAuth: UserAuth = editTextUserAuthInit() 54 | viewModel.signInUserAuth(userAuth = userAuth) 55 | } 56 | } 57 | 58 | binding.textviewRestorePassword.setOnClickListener { 59 | navigateToRestorePasswordFragment() 60 | } 61 | } 62 | 63 | override fun editTextUserAuthInit(): UserAuth { 64 | val userAuth = UserAuth() 65 | userAuth.email = binding.edittextEmail.text.toString() 66 | userAuth.password = binding.edittextPassword.text.toString() 67 | 68 | return userAuth 69 | } 70 | 71 | override fun progressBarSignInObserver() { 72 | viewModel.progressBarSignIn.observe(viewLifecycleOwner) { result -> 73 | if (result) showProgressBarSignIn() 74 | else hideProgressBarSignIn() 75 | } 76 | } 77 | 78 | override fun signInSuccessObserver() { 79 | viewModel.signInSuccess.observe(viewLifecycleOwner) { result -> 80 | when (result) { 81 | is Response.Loading -> {} 82 | is Response.Fail -> showToastLengthLong("Sign in fail: ${result.e}") 83 | is Response.Success -> navigateToLatestMessagesFragment() 84 | } 85 | } 86 | } 87 | 88 | override fun loadRussianEnglishPackObserver() { 89 | viewModel.loadRussianEnglishPack.observe(viewLifecycleOwner) { result -> 90 | when (result) { 91 | is Response.Loading -> {} 92 | is Response.Fail -> showToastLengthLong(text = "Load RU-EN pack false: ${result.e}") 93 | is Response.Success -> {} 94 | } 95 | } 96 | } 97 | 98 | override fun validEditTextShowError(): Boolean { 99 | val email = binding.edittextEmail.text.toString() 100 | val password = binding.edittextPassword.text.toString() 101 | 102 | var valid = false 103 | 104 | when { 105 | email.isEmpty() -> { 106 | binding.edittextEmail.setError("Enter email address.", null) 107 | binding.edittextEmail.requestFocus() 108 | } 109 | !Patterns.EMAIL_ADDRESS.matcher(email).matches() -> { 110 | binding.edittextEmail.setError("Enter valid email address.", null) 111 | binding.edittextEmail.requestFocus() 112 | } 113 | password.isEmpty() -> { 114 | binding.edittextPassword.setError("Enter password.", null) 115 | binding.edittextPassword.requestFocus() 116 | } 117 | else -> { 118 | valid = true 119 | } 120 | } 121 | 122 | return valid 123 | } 124 | 125 | override fun showToastLengthLong(text: String) { 126 | Toast.makeText(activity, text, Toast.LENGTH_LONG) 127 | .show() 128 | } 129 | 130 | override fun showProgressBarSignIn() { 131 | binding.progressbarSignIn.visibility = View.VISIBLE 132 | binding.buttonSignIn.visibility = View.INVISIBLE 133 | } 134 | 135 | override fun hideProgressBarSignIn() { 136 | binding.progressbarSignIn.visibility = View.INVISIBLE 137 | binding.buttonSignIn.visibility = View.VISIBLE 138 | } 139 | 140 | override fun navigateToSignUpFragment() { 141 | findNavController().navigate(R.id.action_signInFragment_to_signUpFragment) 142 | } 143 | 144 | override fun navigateToRestorePasswordFragment() { 145 | findNavController().navigate(R.id.action_signInFragment_to_restorePasswordFragment) 146 | } 147 | 148 | override fun navigateToLatestMessagesFragment() { 149 | findNavController().navigate(R.id.action_signInFragment_to_latestMessagesFragment) 150 | } 151 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/signIn/viewmodel/SignInViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.signIn.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import com.example.dispatch.domain.models.Response 8 | import com.example.dispatch.domain.models.UserAuth 9 | import com.example.dispatch.domain.usecase.DownloadLangRussianEnglishPackUseCase 10 | import com.example.dispatch.domain.usecase.SignInUserAuthUseCase 11 | import com.example.dispatch.presentation.signIn.SignInContract 12 | import dagger.hilt.android.lifecycle.HiltViewModel 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.ExperimentalCoroutinesApi 15 | import kotlinx.coroutines.launch 16 | import javax.inject.Inject 17 | 18 | @HiltViewModel 19 | @ExperimentalCoroutinesApi 20 | class SignInViewModel @Inject constructor( 21 | private val signInUserAuthUseCase: SignInUserAuthUseCase, 22 | private val downloadLangRussianEnglishPackUseCase: DownloadLangRussianEnglishPackUseCase 23 | ) : ViewModel(), SignInContract.SignInViewModel { 24 | 25 | private val _progressBarSignIn = MutableLiveData() 26 | val progressBarSignIn: LiveData = _progressBarSignIn 27 | 28 | private val _signInSuccess = MutableLiveData>() 29 | val signInSuccess: LiveData> = _signInSuccess 30 | 31 | private val _loadRussianEnglishPack = MutableLiveData>() 32 | val loadRussianEnglishPack: LiveData> = _loadRussianEnglishPack 33 | 34 | override fun signInUserAuth(userAuth: UserAuth) { 35 | viewModelScope.launch(Dispatchers.IO) { 36 | signInUserAuthUseCase.execute(userAuth = userAuth).collect { result -> 37 | when (result) { 38 | is Response.Loading -> _progressBarSignIn.postValue(true) 39 | is Response.Fail -> { 40 | _progressBarSignIn.postValue(false) 41 | _signInSuccess.postValue(Response.Fail(e = result.e)) 42 | } 43 | is Response.Success -> { 44 | _progressBarSignIn.postValue(false) 45 | _signInSuccess.postValue(Response.Success(data = true)) 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | override fun downloadLangRussianEnglishPack() { 53 | viewModelScope.launch(Dispatchers.IO) { 54 | downloadLangRussianEnglishPackUseCase.execute().collect { result -> 55 | when (result) { 56 | is Response.Loading -> {} 57 | is Response.Fail -> _loadRussianEnglishPack.postValue(Response.Fail(e = result.e)) 58 | is Response.Success -> {} 59 | } 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/signUp/SignUpContract.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.signUp 2 | 3 | import android.net.Uri 4 | import com.example.dispatch.domain.models.UserAuth 5 | import com.example.dispatch.domain.models.UserDetails 6 | 7 | interface SignUpContract { 8 | interface SignUpFragment { 9 | /** 10 | * Sets the required setOnClickListener on the views fragment 11 | */ 12 | fun setOnClickListeners() 13 | 14 | /** 15 | * Observer cropImageView LiveData from [SignUpViewModel] 16 | */ 17 | fun cropImageViewObserver() 18 | 19 | /** 20 | * Observer progressBarSignUp LiveData from [SignUpViewModel] 21 | */ 22 | fun progressBarSignUpObserver() 23 | 24 | /** 25 | * Observer signUpSuccess LiveData from [SignUpViewModel] 26 | */ 27 | fun signUpSuccessObserver() 28 | 29 | /** 30 | * Start CropImage Activity 31 | */ 32 | fun cropImageActivityStart() 33 | 34 | /** 35 | * Init userDetails variable from [SignUpViewModel] via edittext 36 | */ 37 | fun userDetailsEditTextInit() 38 | 39 | /** 40 | * Checking edittext fields for correctness 41 | * @return - corresponding [Boolean] result 42 | */ 43 | fun validEditTextShowError(): Boolean 44 | 45 | /** 46 | * Takes an imageUri and sets it to the desired view 47 | * @param imageUri - [Uri] 48 | */ 49 | fun setUserImage(imageUri: Uri) 50 | 51 | /** 52 | * Clears cropImageView LiveData from [SignUpViewModel] and manages views 53 | */ 54 | fun deleteUserImageView() 55 | 56 | /** 57 | * Shows progress bar sign up 58 | */ 59 | fun showProgressBarSignUp() 60 | 61 | /** 62 | * Hides progress bar sign up 63 | */ 64 | fun hideProgressBarSignUp() 65 | 66 | /** 67 | * Show toast Toast.LENGTH_LONG type 68 | * @param text - text, shown in toast 69 | */ 70 | fun showToastLengthLong(text: String) 71 | 72 | /** 73 | * Navigate to pop back stack 74 | */ 75 | fun navigateToPopBackStack() 76 | } 77 | 78 | interface SignUpViewModel { 79 | /** 80 | * Sign up a new user in the system 81 | * @param userAuth - [UserAuth] model for sign up 82 | */ 83 | fun signUpUserAuth(userAuth: UserAuth) 84 | 85 | /** 86 | * Get the uid of the current user 87 | */ 88 | fun getCurrentUserUid() 89 | 90 | /** 91 | * Stores the user profile photo in the database 92 | * @param imageUriStr - image uri (string type from cache) 93 | */ 94 | fun saveUserProfileImage(imageUriStr: String) 95 | 96 | /** 97 | * Deletes the user profile photo from the database 98 | */ 99 | fun deleteUserImageProfile() 100 | 101 | /** 102 | * Deletes current user from the system 103 | */ 104 | fun deleteCurrentUserAuth() 105 | 106 | /** 107 | * Saves the userDetails data to the database 108 | * @param userDetails - user data [UserDetails] model 109 | */ 110 | fun saveUserDetails(userDetails: UserDetails) 111 | 112 | /** 113 | * Save user image in LiveData from [SignUpViewModel] 114 | * @param imageUriStr - image uri [String] model from cache 115 | */ 116 | fun saveUserImageLiveData(imageUriStr: String) 117 | 118 | /** 119 | * Clears user image from LiveData from [SignUpViewModel] 120 | */ 121 | fun deleteUserImageLiveData() 122 | } 123 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/splash/SplashScreenContract.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.splash 2 | 3 | interface SplashScreenContract { 4 | interface SplashScreenFragment { 5 | /** 6 | * Observer signInSuccess LiveData from [SplashScreenViewModel] 7 | */ 8 | fun signInSuccessObserver() 9 | 10 | /** 11 | * Navigate to LatestMessagesFragment 12 | */ 13 | fun navigateToLatestMessagesFragment() 14 | 15 | /** 16 | * Navigate to SignInFragment 17 | */ 18 | fun navigateToSignInFragment() 19 | } 20 | 21 | interface SplashScreenViewModel { 22 | /** 23 | * Checking if the user is already logged in 24 | */ 25 | fun checkUserAuthSignedIn() 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/splash/view/SplashScreenFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.splash.view 2 | 3 | import android.annotation.SuppressLint 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.fragment.app.Fragment 9 | import androidx.fragment.app.viewModels 10 | import androidx.navigation.fragment.findNavController 11 | import com.example.dispatch.R 12 | import com.example.dispatch.domain.models.Response 13 | import com.example.dispatch.presentation.splash.SplashScreenContract 14 | import com.example.dispatch.presentation.splash.viewmodel.SplashScreenViewModel 15 | import dagger.hilt.android.AndroidEntryPoint 16 | import kotlinx.coroutines.* 17 | 18 | @AndroidEntryPoint 19 | @ExperimentalCoroutinesApi 20 | @SuppressLint("CustomSplashScreen") 21 | class SplashScreenFragment : Fragment(), SplashScreenContract.SplashScreenFragment { 22 | 23 | private val viewModel: SplashScreenViewModel by viewModels() 24 | 25 | override fun onCreateView( 26 | inflater: LayoutInflater, container: ViewGroup?, 27 | savedInstanceState: Bundle? 28 | ): View? { 29 | return inflater.inflate(R.layout.fragment_splash_screen, container, false) 30 | } 31 | 32 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 33 | CoroutineScope(Dispatchers.Main).launch { 34 | viewModel.checkUserAuthSignedIn() 35 | delay(800) 36 | signInSuccessObserver() 37 | } 38 | } 39 | 40 | override fun signInSuccessObserver() { 41 | viewModel.signInSuccess.observe(viewLifecycleOwner) { result -> 42 | when (result) { 43 | is Response.Loading -> navigateToSignInFragment() 44 | is Response.Fail -> navigateToSignInFragment() 45 | is Response.Success -> navigateToLatestMessagesFragment() 46 | } 47 | } 48 | } 49 | 50 | override fun navigateToLatestMessagesFragment() { 51 | findNavController().navigate(R.id.action_splashScreenFragment_to_latestMessagesFragment) 52 | } 53 | 54 | override fun navigateToSignInFragment() { 55 | findNavController().navigate(R.id.action_splashScreenFragment_to_signInFragment) 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/dispatch/presentation/splash/viewmodel/SplashScreenViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.presentation.splash.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import com.example.dispatch.domain.models.Response 8 | import com.example.dispatch.domain.usecase.CheckUserAuthSignedInUseCase 9 | import com.example.dispatch.presentation.splash.SplashScreenContract 10 | import dagger.hilt.android.lifecycle.HiltViewModel 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.ExperimentalCoroutinesApi 13 | import kotlinx.coroutines.launch 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | @ExperimentalCoroutinesApi 18 | class SplashScreenViewModel @Inject constructor( 19 | private val checkUserAuthSignedInUseCase: CheckUserAuthSignedInUseCase 20 | ) : ViewModel(), SplashScreenContract.SplashScreenViewModel { 21 | 22 | private val _signInSuccess = MutableLiveData>() 23 | val signInSuccess: LiveData> = _signInSuccess 24 | 25 | override fun checkUserAuthSignedIn() { 26 | viewModelScope.launch(Dispatchers.Main) { 27 | checkUserAuthSignedInUseCase.execute().collect { _signInSuccess.postValue(it) } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/splash_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/drawable-v24/splash_screen.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_content_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_content_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_details_messages_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_edittext.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_recipient_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_sender_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_shapeableimageview.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_view_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_account_circle.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_calendar_month.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cleaning_services.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_lock.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_logout.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_mail.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_more_vert.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_remove_circle.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_send.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_translate.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_two_user_circle.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_latest_messages.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 25 | 26 | 37 | 38 | 48 | 49 | 60 | 61 | 67 | 68 | 69 | 70 | 77 | 78 | 87 | 88 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_list_users.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 22 | 23 | 34 | 35 | 42 | 43 | 52 | 53 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_restore_password.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | 27 | 28 | 41 | 42 | 51 | 52 | 53 | 54 | 58 | 59 | 69 | 70 | 76 | 77 | 87 | 88 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_splash_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_container_latest_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 30 | 31 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_container_recipient_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 29 | 30 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_container_sender_message.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | 21 | 29 | 30 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_container_user.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 24 | 25 | 26 | 39 | 40 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/menu/details_messages_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /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/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 16 | 19 | 22 | 23 | 28 | 33 | 38 | 43 | 44 | 49 | 52 | 55 | 56 | 61 | 66 | 67 | 72 | 77 | 80 | 81 | 86 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF000000 4 | #FFFFFFFF 5 | #E14412 6 | 7 | #098E3A 8 | #044008 9 | #212121 10 | #757575 11 | 12 | #ECECEC 13 | #ECECEC 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Dispatch 3 | Hello blank fragment 4 | 5 | Messages 6 | Profile 7 | 8 | - learn English in practice! 9 | Enter email address 10 | Enter password 11 | SIGN UP 12 | SIGN IN 13 | Forgot your account password? Restore! 14 | 15 | CREATE NEW ACCOUNT 16 | ADD IMAGE 17 | Enter fullname 18 | Enter date birth 19 | Enter email 20 | Enter password 21 | Confirm password 22 | BACK 23 | SIGN UP 24 | 25 | RESTORE PASSWORD 26 | Enter email address 27 | BACK 28 | RESTORE 29 | 30 | Change fullname 31 | Change date birth 32 | Change email 33 | Change password 34 | UPDATE PROFILE 35 | CHANGE PASSWORD 36 | SELECT USER 37 | Type a message 38 | Delete messages 39 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/dispatch/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven { url "https://maven.google.com" } 4 | google() 5 | maven { url "https://jitpack.io"} 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.google.gms:google-services:4.3.10' 10 | classpath 'com.google.dagger:hilt-android-gradle-plugin:2.41' 11 | } 12 | } 13 | 14 | plugins { 15 | id 'com.android.application' version '7.1.2' apply false 16 | id 'com.android.library' version '7.1.2' apply false 17 | id 'org.jetbrains.kotlin.android' version '1.6.20' apply false 18 | id 'org.jetbrains.kotlin.jvm' version '1.6.20' apply false 19 | id 'com.google.gms.google-services' version '4.3.10' apply false 20 | } 21 | 22 | allprojects { 23 | repositories { 24 | maven { url "https://maven.google.com" } 25 | google() 26 | maven { url "https://jitpack.io"} 27 | jcenter() 28 | } 29 | } 30 | 31 | task clean(type: Delete) { 32 | delete rootProject.buildDir 33 | } -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /data/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk 32 8 | 9 | defaultConfig { 10 | minSdk 21 11 | targetSdk 32 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles "consumer-rules.pro" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | kotlinOptions { 28 | jvmTarget = '1.8' 29 | } 30 | } 31 | 32 | dependencies { 33 | 34 | implementation 'androidx.core:core-ktx:1.7.0' 35 | implementation 'androidx.appcompat:appcompat:1.4.1' 36 | 37 | testImplementation 'junit:junit:4.13.2' 38 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 39 | 40 | // firebase 41 | implementation 'com.google.firebase:firebase-auth-ktx:21.0.1' 42 | implementation 'com.google.firebase:firebase-database-ktx:20.0.4' 43 | implementation 'com.google.firebase:firebase-storage-ktx:20.0.1' 44 | 45 | // coroutines 46 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' 47 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0" 48 | 49 | // mlkit translate 50 | implementation 'com.google.mlkit:translate:17.0.0' 51 | implementation 'com.google.mlkit:language-id:17.0.3' 52 | 53 | implementation project(path: ':domain') 54 | } -------------------------------------------------------------------------------- /data/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/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/androidTest/java/com/example/dispatch/data/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.example.dispatch.data.test", appContext.packageName) 21 | } 22 | } -------------------------------------------------------------------------------- /data/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/repository/MessageRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.repository 2 | 3 | import com.example.dispatch.data.storage.MessageStorage 4 | import com.example.dispatch.domain.models.FromToUser 5 | import com.example.dispatch.domain.models.Message 6 | import com.example.dispatch.domain.models.Response 7 | import com.example.dispatch.domain.repository.MessageRepository 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi 9 | import kotlinx.coroutines.flow.Flow 10 | 11 | @ExperimentalCoroutinesApi 12 | class MessageRepositoryImpl(private val messageStorage: MessageStorage) : MessageRepository { 13 | override suspend fun save(message: Message): Flow> { 14 | return messageStorage.save(message = message) 15 | } 16 | 17 | override suspend fun saveLatestMessage(message: Message): Flow> { 18 | return messageStorage.saveLatestMessage(message = message) 19 | } 20 | 21 | override suspend fun listenFromToUserMessages(fromToUser: FromToUser): Flow> { 22 | return messageStorage.listenFromToUserMessages(fromToUser = fromToUser) 23 | } 24 | 25 | override suspend fun deleteDialogBothUsers(fromToUser: FromToUser): Flow> { 26 | return messageStorage.deleteDialogBothUsers(fromToUser = fromToUser) 27 | } 28 | 29 | override suspend fun deleteLatestMessageBothUsers(fromToUser: FromToUser): Flow> { 30 | return messageStorage.deleteLatestMessageBothUsers(fromToUser = fromToUser) 31 | } 32 | 33 | override suspend fun getLatestMessages(fromUserUid: String): Flow>> { 34 | return messageStorage.getLatestMessages(fromUserUid = fromUserUid) 35 | } 36 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/repository/TranslateRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.repository 2 | 3 | import com.example.dispatch.data.storage.TranslateStorage 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.repository.TranslateRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class TranslateRepositoryImpl(private val translateStorage: TranslateStorage) : 11 | TranslateRepository { 12 | override suspend fun downloadLangRussianEnglishPack(): Flow> { 13 | return translateStorage.downloadLangRussianEnglishPack() 14 | } 15 | 16 | override suspend fun translateRussianEnglishText(text: String): Flow> { 17 | return translateStorage.translateRussianEnglishText(text = text) 18 | } 19 | 20 | override suspend fun translateEnglishRussianText(text: String): Flow> { 21 | return translateStorage.translateEnglishRussianText(text = text) 22 | } 23 | 24 | override suspend fun languageIdentifier(text: String): Flow> { 25 | return translateStorage.languageIndentifier(text = text) 26 | } 27 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/repository/UserAuthRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.repository 2 | 3 | import com.example.dispatch.data.storage.UserAuthStorage 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.models.UserAuth 6 | import com.example.dispatch.domain.repository.UserAuthRepository 7 | import kotlinx.coroutines.ExperimentalCoroutinesApi 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | @ExperimentalCoroutinesApi 11 | class UserAuthRepositoryImpl(private val userAuthStorage: UserAuthStorage) : UserAuthRepository { 12 | override suspend fun login(userAuth: UserAuth): Flow> { 13 | return userAuthStorage.login(userAuth = userAuth) 14 | } 15 | 16 | override suspend fun register(userAuth: UserAuth): Flow> { 17 | return userAuthStorage.register(userAuth = userAuth) 18 | } 19 | 20 | override suspend fun checkSignedIn(): Flow> { 21 | return userAuthStorage.checkSignedIn() 22 | } 23 | 24 | override suspend fun getCurrentUserUid(): Flow> { 25 | return userAuthStorage.getCurrentUserUid() 26 | } 27 | 28 | override suspend fun deleteCurrentUser(): Flow> { 29 | return userAuthStorage.deleteCurrentUser() 30 | } 31 | 32 | override suspend fun restorePasswordByEmail(email: String): Flow> { 33 | return userAuthStorage.restorePasswordByEmail(email = email) 34 | } 35 | 36 | override suspend fun changeEmail( 37 | userAuth: UserAuth, 38 | newEmail: String 39 | ): Flow> { 40 | return userAuthStorage.changeEmail(userAuth = userAuth, newEmail = newEmail) 41 | } 42 | 43 | override suspend fun changePassword( 44 | userAuth: UserAuth, 45 | newPassword: String 46 | ): Flow> { 47 | return userAuthStorage.changePassword(userAuth = userAuth, newPassword = newPassword) 48 | } 49 | 50 | override suspend fun signOut(): Flow> { 51 | return userAuthStorage.signOut() 52 | } 53 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/repository/UserDetailsRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.repository 2 | 3 | import com.example.dispatch.data.storage.UserDetailsStorage 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.models.UserDetails 6 | import com.example.dispatch.domain.models.UserDetailsPublic 7 | import com.example.dispatch.domain.repository.UserDetailsRepository 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi 9 | import kotlinx.coroutines.flow.Flow 10 | 11 | @ExperimentalCoroutinesApi 12 | class UserDetailsRepositoryImpl(private val userDetailsStorage: UserDetailsStorage) : 13 | UserDetailsRepository { 14 | override suspend fun save(userDetails: UserDetails): Flow> { 15 | return userDetailsStorage.save(userDetails = userDetails) 16 | } 17 | 18 | override suspend fun getCurrentUser(): Flow> { 19 | return userDetailsStorage.getCurrentUser() 20 | } 21 | 22 | override suspend fun deleteCurrentUser(): Flow> { 23 | return userDetailsStorage.deleteCurrentUser() 24 | } 25 | 26 | override suspend fun changeFullname(newFullname: String): Flow> { 27 | return userDetailsStorage.changeFullname(newFullname = newFullname) 28 | } 29 | 30 | override suspend fun changeImageProfileUri(newImageUriStr: String): Flow> { 31 | return userDetailsStorage.changeImageProfileUri(newImageUriStr = newImageUriStr) 32 | } 33 | 34 | override suspend fun changeEmailAddress(newEmail: String): Flow> { 35 | return userDetailsStorage.changeEmailAddress(newEmail = newEmail) 36 | } 37 | 38 | override suspend fun changePassword(newPassword: String): Flow> { 39 | return userDetailsStorage.changePassword(newPassword = newPassword) 40 | } 41 | 42 | override suspend fun getUsersList(): Flow>> { 43 | return userDetailsStorage.getUsersList() 44 | } 45 | 46 | override suspend fun getUserDetailsPublicOnUid(uid: String): Flow> { 47 | return userDetailsStorage.getUserDetailsPublicOnUid(uid = uid) 48 | } 49 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/repository/UserImagesRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.repository 2 | 3 | import com.example.dispatch.data.storage.UserImagesStorage 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.repository.UserImagesRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class UserImagesRepositoryImpl(private val userImagesStorage: UserImagesStorage) : 11 | UserImagesRepository { 12 | override suspend fun saveImageProfile(newImageUriStr: String): Flow> { 13 | return userImagesStorage.saveImageProfile(newImageUriStr = newImageUriStr) 14 | } 15 | 16 | override suspend fun deleteImageProfile(): Flow> { 17 | return userImagesStorage.deleteImageProfile() 18 | } 19 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/storage/MessageStorage.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.storage 2 | 3 | import com.example.dispatch.domain.models.FromToUser 4 | import com.example.dispatch.domain.models.Message 5 | import com.example.dispatch.domain.models.Response 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | interface MessageStorage { 9 | suspend fun save(message: Message): Flow> 10 | 11 | suspend fun saveLatestMessage(message: Message): Flow> 12 | 13 | suspend fun listenFromToUserMessages(fromToUser: FromToUser): Flow> 14 | 15 | suspend fun deleteDialogBothUsers(fromToUser: FromToUser): Flow> 16 | 17 | suspend fun deleteLatestMessageBothUsers(fromToUser: FromToUser): Flow> 18 | 19 | suspend fun getLatestMessages(fromUserUid: String): Flow>> 20 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/storage/TranslateStorage.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.storage 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.flow.Flow 6 | 7 | @ExperimentalCoroutinesApi 8 | interface TranslateStorage { 9 | suspend fun downloadLangRussianEnglishPack(): Flow> 10 | 11 | suspend fun translateRussianEnglishText(text: String): Flow> 12 | 13 | suspend fun translateEnglishRussianText(text: String): Flow> 14 | 15 | suspend fun languageIndentifier(text: String): Flow> 16 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/storage/UserAuthStorage.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.storage 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserAuth 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | interface UserAuthStorage { 10 | suspend fun login(userAuth: UserAuth): Flow> 11 | 12 | suspend fun register(userAuth: UserAuth): Flow> 13 | 14 | suspend fun checkSignedIn(): Flow> 15 | 16 | suspend fun getCurrentUserUid(): Flow> 17 | 18 | suspend fun deleteCurrentUser(): Flow> 19 | 20 | suspend fun restorePasswordByEmail(email: String): Flow> 21 | 22 | suspend fun changeEmail(userAuth: UserAuth, newEmail: String): Flow> 23 | 24 | suspend fun changePassword(userAuth: UserAuth, newPassword: String): Flow> 25 | 26 | suspend fun signOut(): Flow> 27 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/storage/UserDetailsStorage.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.storage 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserDetails 5 | import com.example.dispatch.domain.models.UserDetailsPublic 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | interface UserDetailsStorage { 11 | suspend fun save(userDetails: UserDetails): Flow> 12 | 13 | suspend fun getCurrentUser(): Flow> 14 | 15 | suspend fun deleteCurrentUser(): Flow> 16 | 17 | suspend fun changeImageProfileUri(newImageUriStr: String): Flow> 18 | 19 | suspend fun changeFullname(newFullname: String): Flow> 20 | 21 | suspend fun changeEmailAddress(newEmail: String): Flow> 22 | 23 | suspend fun changePassword(newPassword: String): Flow> 24 | 25 | suspend fun getUsersList(): Flow>> 26 | 27 | suspend fun getUserDetailsPublicOnUid(uid: String): Flow> 28 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/storage/UserImagesStorage.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.storage 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.flow.Flow 6 | 7 | @ExperimentalCoroutinesApi 8 | interface UserImagesStorage { 9 | suspend fun saveImageProfile(newImageUriStr: String): Flow> 10 | 11 | suspend fun deleteImageProfile(): Flow> 12 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/storage/firebase/FirebaseUserAuthStorage.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.storage.firebase 2 | 3 | import com.example.dispatch.data.storage.UserAuthStorage 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.models.UserAuth 6 | import com.google.firebase.auth.FirebaseAuth 7 | import kotlinx.coroutines.ExperimentalCoroutinesApi 8 | import kotlinx.coroutines.cancel 9 | import kotlinx.coroutines.channels.awaitClose 10 | import kotlinx.coroutines.flow.Flow 11 | import kotlinx.coroutines.flow.callbackFlow 12 | 13 | @ExperimentalCoroutinesApi 14 | class FirebaseUserAuthStorage : UserAuthStorage { 15 | companion object { 16 | private val fAuth = FirebaseAuth.getInstance() 17 | } 18 | 19 | override suspend fun login(userAuth: UserAuth): Flow> = callbackFlow { 20 | trySend(Response.Loading()) 21 | 22 | fAuth.signInWithEmailAndPassword(userAuth.email, userAuth.password) 23 | .addOnSuccessListener { 24 | trySend(Response.Success(data = true)) 25 | }.addOnFailureListener { e -> 26 | trySend(Response.Fail(e = e)) 27 | } 28 | 29 | awaitClose { this.cancel() } 30 | } 31 | 32 | override suspend fun register(userAuth: UserAuth): Flow> = callbackFlow { 33 | trySend(Response.Loading()) 34 | 35 | fAuth.createUserWithEmailAndPassword(userAuth.email, userAuth.password) 36 | .addOnSuccessListener { 37 | trySend(Response.Success(data = true)) 38 | }.addOnFailureListener { e -> 39 | trySend(Response.Fail(e = e)) 40 | } 41 | 42 | awaitClose { this.cancel() } 43 | } 44 | 45 | override suspend fun checkSignedIn(): Flow> = callbackFlow { 46 | trySend(Response.Loading()) 47 | 48 | val currentUser = fAuth.currentUser 49 | 50 | if (currentUser != null) { 51 | trySend(Response.Success(data = true)) 52 | } else { 53 | trySend(Response.Fail(Exception("current user = null"))) 54 | } 55 | 56 | awaitClose { this.cancel() } 57 | } 58 | 59 | override suspend fun getCurrentUserUid(): Flow> = callbackFlow { 60 | trySend(Response.Loading()) 61 | 62 | try { 63 | val uidCurrentUser = fAuth.currentUser?.uid.toString() 64 | trySend(Response.Success(data = uidCurrentUser)) 65 | } catch (e: Exception) { 66 | trySend(Response.Fail(e = e)) 67 | } 68 | 69 | awaitClose { this.cancel() } 70 | } 71 | 72 | override suspend fun deleteCurrentUser(): Flow> = callbackFlow { 73 | trySend(Response.Loading()) 74 | 75 | fAuth.currentUser?.delete() 76 | ?.addOnSuccessListener { 77 | trySend(Response.Success(data = true)) 78 | }?.addOnFailureListener { e -> 79 | trySend(Response.Fail(e = e)) 80 | } 81 | 82 | awaitClose { this.cancel() } 83 | } 84 | 85 | override suspend fun restorePasswordByEmail(email: String): Flow> = 86 | callbackFlow { 87 | trySend(Response.Loading()) 88 | 89 | fAuth.sendPasswordResetEmail(email) 90 | .addOnSuccessListener { 91 | trySend(Response.Success(data = true)) 92 | }.addOnFailureListener { e -> 93 | trySend(Response.Fail(e = e)) 94 | } 95 | 96 | awaitClose { this.cancel() } 97 | } 98 | 99 | override suspend fun changeEmail( 100 | userAuth: UserAuth, 101 | newEmail: String 102 | ): Flow> = callbackFlow { 103 | trySend(Response.Loading()) 104 | 105 | fAuth.signInWithEmailAndPassword(userAuth.email, userAuth.password) 106 | fAuth.currentUser?.updateEmail(newEmail) 107 | ?.addOnSuccessListener { 108 | trySend(Response.Success(data = true)) 109 | }?.addOnFailureListener { e -> 110 | trySend(Response.Fail(e = e)) 111 | } 112 | 113 | awaitClose { this.cancel() } 114 | } 115 | 116 | override suspend fun changePassword( 117 | userAuth: UserAuth, 118 | newPassword: String 119 | ): Flow> = callbackFlow { 120 | trySend(Response.Loading()) 121 | 122 | fAuth.signInWithEmailAndPassword(userAuth.email, userAuth.password) 123 | fAuth.currentUser?.updatePassword(newPassword) 124 | ?.addOnSuccessListener { 125 | trySend(Response.Success(data = true)) 126 | }?.addOnFailureListener { e -> 127 | trySend(Response.Fail(e = e)) 128 | } 129 | 130 | awaitClose { this.cancel() } 131 | } 132 | 133 | override suspend fun signOut(): Flow> = callbackFlow { 134 | trySend(Response.Loading()) 135 | 136 | fAuth.signOut() 137 | trySend(Response.Success(data = true)) 138 | 139 | awaitClose { this.cancel() } 140 | } 141 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/storage/firebase/FirebaseUserImagesStorage.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.storage.firebase 2 | 3 | import android.net.Uri 4 | import com.example.dispatch.data.storage.UserImagesStorage 5 | import com.example.dispatch.domain.models.Response 6 | import com.google.firebase.auth.FirebaseAuth 7 | import com.google.firebase.storage.FirebaseStorage 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi 9 | import kotlinx.coroutines.cancel 10 | import kotlinx.coroutines.channels.awaitClose 11 | import kotlinx.coroutines.flow.Flow 12 | import kotlinx.coroutines.flow.callbackFlow 13 | 14 | @ExperimentalCoroutinesApi 15 | class FirebaseUserImagesStorage : UserImagesStorage { 16 | companion object { 17 | private val fAuth = FirebaseAuth.getInstance() 18 | private val fStorage = FirebaseStorage.getInstance() 19 | } 20 | 21 | override suspend fun saveImageProfile(newImageUriStr: String): Flow> = 22 | callbackFlow { 23 | trySend(Response.Loading()) 24 | 25 | val uidCurrentUser = fAuth.currentUser?.uid.toString() 26 | val refImage = fStorage.getReference("/$uidCurrentUser/profile.jpg") 27 | val imageProfileUri: Uri = Uri.parse(newImageUriStr) 28 | 29 | refImage.putFile(imageProfileUri).addOnCompleteListener { 30 | refImage.downloadUrl.addOnSuccessListener { uri -> 31 | val uriStr = uri.toString() 32 | trySend(Response.Success(data = uriStr)) 33 | }.addOnFailureListener { e -> 34 | trySend(Response.Fail(e = e)) 35 | } 36 | }.addOnFailureListener { e -> 37 | trySend(Response.Fail(e = e)) 38 | } 39 | 40 | awaitClose { this.cancel() } 41 | } 42 | 43 | override suspend fun deleteImageProfile(): Flow> = callbackFlow { 44 | trySend(Response.Loading()) 45 | 46 | val uidCurrentUser = fAuth.currentUser?.uid.toString() 47 | val refImage = fStorage.getReference("/$uidCurrentUser/profile.jpg") 48 | 49 | refImage.delete() 50 | .addOnSuccessListener { 51 | trySend(Response.Success(data = true)) 52 | }.addOnFailureListener { e -> 53 | trySend(Response.Fail(e = e)) 54 | } 55 | 56 | awaitClose { this.cancel() } 57 | } 58 | } -------------------------------------------------------------------------------- /data/src/main/java/com/example/dispatch/data/storage/mlkit/MlKitTranslateStorage.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data.storage.mlkit 2 | 3 | import com.example.dispatch.data.storage.TranslateStorage 4 | import com.example.dispatch.domain.constants.LanguageCodeConstants 5 | import com.example.dispatch.domain.models.Response 6 | import com.google.mlkit.nl.languageid.LanguageIdentification 7 | import com.google.mlkit.nl.translate.TranslateLanguage 8 | import com.google.mlkit.nl.translate.Translation 9 | import com.google.mlkit.nl.translate.TranslatorOptions 10 | import kotlinx.coroutines.ExperimentalCoroutinesApi 11 | import kotlinx.coroutines.cancel 12 | import kotlinx.coroutines.channels.awaitClose 13 | import kotlinx.coroutines.flow.Flow 14 | import kotlinx.coroutines.flow.callbackFlow 15 | 16 | @ExperimentalCoroutinesApi 17 | class MlKitTranslateStorage : TranslateStorage { 18 | override suspend fun downloadLangRussianEnglishPack(): Flow> = callbackFlow { 19 | trySend(Response.Loading()) 20 | 21 | val options = TranslatorOptions.Builder() 22 | .setSourceLanguage(TranslateLanguage.RUSSIAN) 23 | .setTargetLanguage(TranslateLanguage.ENGLISH) 24 | .build() 25 | val russianEnglishTranslator = Translation.getClient(options) 26 | 27 | russianEnglishTranslator.downloadModelIfNeeded() 28 | .addOnSuccessListener { 29 | trySend(Response.Success(data = true)) 30 | }.addOnFailureListener { e -> 31 | trySend(Response.Fail(e = e)) 32 | } 33 | 34 | awaitClose { this.cancel() } 35 | } 36 | 37 | override suspend fun translateRussianEnglishText(text: String): Flow> = 38 | callbackFlow { 39 | trySend(Response.Loading()) 40 | 41 | val options = TranslatorOptions.Builder() 42 | .setSourceLanguage(TranslateLanguage.RUSSIAN) 43 | .setTargetLanguage(TranslateLanguage.ENGLISH) 44 | .build() 45 | val russianEnglishTranslator = Translation.getClient(options) 46 | 47 | russianEnglishTranslator.downloadModelIfNeeded() 48 | .addOnSuccessListener { 49 | russianEnglishTranslator.translate(text) 50 | .addOnSuccessListener { textTranslated -> 51 | trySend(Response.Success(data = textTranslated)) 52 | }.addOnFailureListener { e -> 53 | trySend(Response.Fail(e = e)) 54 | } 55 | }.addOnFailureListener { e -> 56 | trySend(Response.Fail(e = e)) 57 | } 58 | 59 | awaitClose { this.cancel() } 60 | } 61 | 62 | override suspend fun translateEnglishRussianText(text: String): Flow> = 63 | callbackFlow { 64 | trySend(Response.Loading()) 65 | 66 | val options = TranslatorOptions.Builder() 67 | .setSourceLanguage(TranslateLanguage.ENGLISH) 68 | .setTargetLanguage(TranslateLanguage.RUSSIAN) 69 | .build() 70 | val englishRussianTranslator = Translation.getClient(options) 71 | 72 | englishRussianTranslator.downloadModelIfNeeded() 73 | .addOnSuccessListener { 74 | englishRussianTranslator.translate(text) 75 | .addOnSuccessListener { textTranslated -> 76 | trySend(Response.Success(data = textTranslated)) 77 | }.addOnFailureListener { e -> 78 | trySend(Response.Fail(e = e)) 79 | } 80 | }.addOnFailureListener { e -> 81 | trySend(Response.Fail(e = e)) 82 | } 83 | 84 | awaitClose { this.cancel() } 85 | } 86 | 87 | override suspend fun languageIndentifier(text: String): Flow> = callbackFlow { 88 | trySend(Response.Loading()) 89 | 90 | val languageIdentifier = LanguageIdentification.getClient() 91 | 92 | languageIdentifier.identifyLanguage(text) 93 | .addOnSuccessListener { languageCode -> 94 | when (languageCode) { 95 | LanguageCodeConstants.EN -> trySend(Response.Success(data = LanguageCodeConstants.EN)) 96 | LanguageCodeConstants.RU -> trySend(Response.Success(data = LanguageCodeConstants.RU)) 97 | else -> trySend(Response.Success(data = languageCode)) 98 | } 99 | } 100 | .addOnFailureListener { e -> 101 | trySend(Response.Fail(e = e)) 102 | } 103 | 104 | awaitClose { this.cancel() } 105 | } 106 | } -------------------------------------------------------------------------------- /data/src/test/java/com/example/dispatch/data/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.data 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } -------------------------------------------------------------------------------- /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_7 8 | targetCompatibility = JavaVersion.VERSION_1_7 9 | } 10 | 11 | dependencies { 12 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0" 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/constants/LanguageCodeConstants.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.constants 2 | 3 | sealed class LanguageCodeConstants { 4 | companion object { 5 | const val EN = "en" 6 | const val RU = "ru" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/models/FromToUser.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.models 2 | 3 | data class FromToUser( 4 | val fromUserUid: String = "", 5 | val toUserUid: String = "" 6 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/models/Message.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.models 2 | 3 | data class Message( 4 | val russianMessage: String = "", 5 | val englishMessage: String = "", 6 | val timestamp: Long = 0, 7 | val fromUserUid: String = "", 8 | val toUserUid: String = "" 9 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/models/Response.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.models 2 | 3 | sealed class Response { 4 | class Loading : Response() 5 | data class Success(val data: T) : Response() 6 | data class Fail(val e: Exception) : Response() 7 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/models/UserAuth.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.models 2 | 3 | data class UserAuth( 4 | var email: String = "", 5 | var password: String = "" 6 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/models/UserDetails.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.models 2 | 3 | data class UserDetails( 4 | var uid: String = "", 5 | var fullname: String = "", 6 | var email: String = "", 7 | var password: String = "", 8 | var photoProfileUrl: String = "" 9 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/models/UserDetailsPublic.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.models 2 | 3 | data class UserDetailsPublic( 4 | var uid: String = "", 5 | var fullname: String = "", 6 | var email: String = "", 7 | var photoProfileUrl: String = "" 8 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/repository/MessageRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.repository 2 | 3 | import com.example.dispatch.domain.models.FromToUser 4 | import com.example.dispatch.domain.models.Message 5 | import com.example.dispatch.domain.models.Response 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | interface MessageRepository { 11 | /** 12 | * Save message in the database 13 | * @param message - [Message] 14 | * @return [Boolean] value result operation 15 | */ 16 | suspend fun save(message: Message): Flow> 17 | 18 | /** 19 | * Save latest message in the database 20 | * @param message - [Message] 21 | * @return [Boolean] value result operation 22 | */ 23 | suspend fun saveLatestMessage(message: Message): Flow> 24 | 25 | /** 26 | * Gets messages between users (if any) 27 | * @param - [FromToUser] 28 | * @return [Message] 29 | */ 30 | suspend fun listenFromToUserMessages(fromToUser: FromToUser): Flow> 31 | 32 | /** 33 | * Delete all messages between two users 34 | * @param fromToUser - [FromToUser] 35 | * @return [Boolean] value result operation 36 | */ 37 | suspend fun deleteDialogBothUsers(fromToUser: FromToUser): Flow> 38 | 39 | /** 40 | * Removes data from recent messages 41 | * @param fromToUser - [FromToUser] 42 | * @return [Boolean] value result operation 43 | */ 44 | suspend fun deleteLatestMessageBothUsers(fromToUser: FromToUser): Flow> 45 | 46 | /** 47 | * Gets latest messages between users 48 | * @param fromUserUid - [String] user uid 49 | * @return [ArrayList]-[Message] all the latest messages of the user 50 | */ 51 | suspend fun getLatestMessages(fromUserUid: String): Flow>> 52 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/repository/TranslateRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.repository 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.flow.Flow 6 | 7 | @ExperimentalCoroutinesApi 8 | interface TranslateRepository { 9 | /** 10 | * Downloads en-ru language pack 11 | * @return [Boolean] value success operation 12 | */ 13 | suspend fun downloadLangRussianEnglishPack(): Flow> 14 | 15 | /** 16 | * Translates text from Russian into English 17 | * @param text - [String] russian text 18 | * @return [String] english text 19 | */ 20 | suspend fun translateRussianEnglishText(text: String): Flow> 21 | 22 | /** 23 | * Translates text from English into Russian 24 | * @param text - [String] english text 25 | * @return [String] russian text 26 | */ 27 | suspend fun translateEnglishRussianText(text: String): Flow> 28 | 29 | /** 30 | * Specifies the source language of the text 31 | * @param text - [String] 32 | * @return [String], language code according to BCP-47 language code 33 | */ 34 | suspend fun languageIdentifier(text: String): Flow> 35 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/repository/UserAuthRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.repository 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserAuth 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | /** 9 | * The repository provides work with the user of the [UserAuth] class 10 | */ 11 | @ExperimentalCoroutinesApi 12 | interface UserAuthRepository { 13 | /** 14 | * The function returns a [Boolean] value of the user's authorization attempt 15 | * @param userAuth - accepts email and password as by [UserAuth] class for authorization 16 | */ 17 | suspend fun login(userAuth: UserAuth): Flow> 18 | 19 | /** 20 | * Function returns [Boolean] value of new user registration attempt 21 | * @param userAuth - accepts login and password by [UserAuth] class for registration 22 | */ 23 | suspend fun register(userAuth: UserAuth): Flow> 24 | 25 | /** 26 | * The function returns a [Boolean] value whether the user is logged in 27 | */ 28 | suspend fun checkSignedIn(): Flow> 29 | 30 | /** 31 | * The function returns the [String] uid of the current user 32 | */ 33 | suspend fun getCurrentUserUid(): Flow> 34 | 35 | /** 36 | * The function returns the [Boolean] value of deleting the current user 37 | */ 38 | suspend fun deleteCurrentUser(): Flow> 39 | 40 | /** 41 | * The function returns the [Boolean] value of an attempt to recover the password from the email address 42 | * note: the user's email will receive instructions for resetting the password 43 | * @param email - user's email as a [String] 44 | */ 45 | suspend fun restorePasswordByEmail(email: String): Flow> 46 | 47 | /** 48 | * The function returns a [Boolean] value of an attempt to change the user's email 49 | * To change your email address, you need to authenticate the user 50 | * @param userAuth - login / password user value as [UserAuth] 51 | * @param newEmail - new email address 52 | */ 53 | suspend fun changeEmail(userAuth: UserAuth, newEmail: String): Flow> 54 | 55 | /** 56 | * The function returns a [Boolean] value of an attempt to change the user's password 57 | * To change your password, you need to authenticate the user 58 | * @param userAuth - login / password user value as [UserAuth] 59 | * @param newPassword - new password 60 | */ 61 | suspend fun changePassword(userAuth: UserAuth, newPassword: String): Flow> 62 | 63 | /** 64 | * The function returns a [Boolean] value of the user's logout attempt 65 | */ 66 | suspend fun signOut(): Flow> 67 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/repository/UserDetailsRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.repository 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserDetails 5 | import com.example.dispatch.domain.models.UserDetailsPublic 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | /** 10 | * The repository provides work with the [UserDetails] class 11 | */ 12 | @ExperimentalCoroutinesApi 13 | interface UserDetailsRepository { 14 | /** 15 | * The function returns a [Boolean] value of attempt to save user data 16 | * @param userDetails - user's personal data to be saved as [UserDetails] class 17 | */ 18 | suspend fun save(userDetails: UserDetails): Flow> 19 | 20 | /** 21 | * The function returns the data of the current authorized user in the form of the [UserDetails] class 22 | */ 23 | suspend fun getCurrentUser(): Flow> 24 | 25 | /** 26 | * The function returns a [Boolean] value of an attempt to delete the user's personal data 27 | */ 28 | suspend fun deleteCurrentUser(): Flow> 29 | 30 | /** 31 | * Function returns [Boolean] value of attempt to change photoProfileUrl in saved [UserDetails] 32 | * @param newImageUriStr - link to new user photo 33 | */ 34 | suspend fun changeImageProfileUri(newImageUriStr: String): Flow> 35 | 36 | /** 37 | * Function returns [Boolean] value of attempt to change fullname in saved [UserDetails] 38 | * @param newFullname - new user fullname 39 | */ 40 | suspend fun changeFullname(newFullname: String): Flow> 41 | 42 | /** 43 | * Function returns [Boolean] value of attempt to change email address in saved [UserDetails] 44 | * @param newEmail - new user email 45 | */ 46 | suspend fun changeEmailAddress(newEmail: String): Flow> 47 | 48 | /** 49 | * Function returns [Boolean] value of attempt to change password in saved [UserDetails] 50 | * @param newPassword - new user password 51 | */ 52 | suspend fun changePassword(newPassword: String): Flow> 53 | 54 | /** 55 | * Get all users list 56 | * @return [ArrayList]-[UserDetailsPublic] 57 | */ 58 | suspend fun getUsersList(): Flow>> 59 | 60 | /** 61 | * Getting details of a specific user by uid 62 | * @return [UserDetailsPublic] model 63 | */ 64 | suspend fun getUserDetailsPublicOnUid(uid: String): Flow> 65 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/repository/UserImagesRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.repository 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.flow.Flow 6 | 7 | /** 8 | * The repository provides work with user images 9 | */ 10 | @ExperimentalCoroutinesApi 11 | interface UserImagesRepository { 12 | /** 13 | * The function returns a link to the user's saved main image as a [String] 14 | * @param newImageUriStr - cached image link (uri to string) 15 | */ 16 | suspend fun saveImageProfile(newImageUriStr: String): Flow> 17 | 18 | /** 19 | * The function returns a [Boolean] value of an attempt to delete the profile photo of the user 20 | */ 21 | suspend fun deleteImageProfile(): Flow> 22 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/ChangeUserAuthEmailUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserAuth 5 | import com.example.dispatch.domain.repository.UserAuthRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class ChangeUserAuthEmailUseCase(private val userAuthRepository: UserAuthRepository) { 11 | suspend fun execute(userAuth: UserAuth, newEmail: String): Flow> { 12 | return userAuthRepository.changeEmail(userAuth = userAuth, newEmail = newEmail) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/ChangeUserAuthPasswordUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserAuth 5 | import com.example.dispatch.domain.repository.UserAuthRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class ChangeUserAuthPasswordUseCase(private val userAuthRepository: UserAuthRepository) { 11 | suspend fun execute(userAuth: UserAuth, newPassword: String): Flow> { 12 | return userAuthRepository.changePassword(userAuth = userAuth, newPassword = newPassword) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/ChangeUserDetailsEmailUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserDetailsRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class ChangeUserDetailsEmailUseCase(private val userDetailsRepository: UserDetailsRepository) { 10 | suspend fun execute(newEmail: String): Flow> { 11 | return userDetailsRepository.changeEmailAddress(newEmail = newEmail) 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/ChangeUserDetailsFullnameUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserDetailsRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class ChangeUserDetailsFullnameUseCase(private val userDetailsRepository: UserDetailsRepository) { 10 | suspend fun execute(newFullname: String): Flow> { 11 | return userDetailsRepository.changeFullname(newFullname = newFullname) 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/ChangeUserDetailsPasswordUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserDetailsRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class ChangeUserDetailsPasswordUseCase(private val userDetailsRepository: UserDetailsRepository) { 10 | suspend fun execute(newPassword: String): Flow> { 11 | return userDetailsRepository.changePassword(newPassword = newPassword) 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/ChangeUserDetailsPhotoProfileUrlUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserDetailsRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class ChangeUserDetailsPhotoProfileUrlUseCase(private val userDetailsRepository: UserDetailsRepository) { 10 | suspend fun execute(newImageUriStr: String): Flow> { 11 | return userDetailsRepository.changeImageProfileUri(newImageUriStr = newImageUriStr) 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/CheckUserAuthSignedInUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserAuthRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class CheckUserAuthSignedInUseCase(private val userAuthRepository: UserAuthRepository) { 10 | suspend fun execute(): Flow> { 11 | return userAuthRepository.checkSignedIn() 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/DeleteCurrentUserAuthUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserAuthRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class DeleteCurrentUserAuthUseCase(private val userAuthRepository: UserAuthRepository) { 10 | suspend fun execute(): Flow> { 11 | return userAuthRepository.deleteCurrentUser() 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/DeleteCurrentUserDetailsUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserDetailsRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class DeleteCurrentUserDetailsUseCase(private val userDetailsRepository: UserDetailsRepository) { 10 | suspend fun execute(): Flow> { 11 | return userDetailsRepository.deleteCurrentUser() 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/DeleteDialogBothUsersUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.FromToUser 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.repository.MessageRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class DeleteDialogBothUsersUseCase(private val messageRepository: MessageRepository) { 11 | suspend fun execute(fromToUser: FromToUser): Flow> { 12 | return messageRepository.deleteDialogBothUsers(fromToUser = fromToUser) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/DeleteLatestMessagesBothUsersUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.FromToUser 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.repository.MessageRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class DeleteLatestMessagesBothUsersUseCase(private val messageRepository: MessageRepository) { 11 | suspend fun execute(fromToUser: FromToUser): Flow> { 12 | return messageRepository.deleteLatestMessageBothUsers(fromToUser = fromToUser) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/DeleteUserImageProfileUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserImagesRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class DeleteUserImageProfileUseCase(private val userImagesRepository: UserImagesRepository) { 10 | suspend fun execute(): Flow> { 11 | return userImagesRepository.deleteImageProfile() 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/DownloadLangRussianEnglishPackUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.TranslateRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class DownloadLangRussianEnglishPackUseCase(private val translateRepository: TranslateRepository) { 10 | suspend fun execute(): Flow> { 11 | return translateRepository.downloadLangRussianEnglishPack() 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/GetCurrentUserDetailsUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserDetails 5 | import com.example.dispatch.domain.repository.UserDetailsRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class GetCurrentUserDetailsUseCase(private val userDetailsRepository: UserDetailsRepository) { 11 | suspend fun execute(): Flow> { 12 | return userDetailsRepository.getCurrentUser() 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/GetCurrentUserUidUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserAuthRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class GetCurrentUserUidUseCase(private val userAuthRepository: UserAuthRepository) { 10 | suspend fun execute(): Flow> { 11 | return userAuthRepository.getCurrentUserUid() 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/GetLatestMessagesUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Message 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.repository.MessageRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class GetLatestMessagesUseCase(private val messageRepository: MessageRepository) { 11 | suspend fun execute(fromUserUid: String): Flow>> { 12 | return messageRepository.getLatestMessages(fromUserUid = fromUserUid) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/GetUserDetailsPublicOnUidUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserDetailsPublic 5 | import com.example.dispatch.domain.repository.UserDetailsRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class GetUserDetailsPublicOnUidUseCase(private val userDetailsRepository: UserDetailsRepository) { 11 | suspend fun execute(uid: String): Flow> { 12 | return userDetailsRepository.getUserDetailsPublicOnUid(uid = uid) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/GetUsersListUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserDetailsPublic 5 | import com.example.dispatch.domain.repository.UserDetailsRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class GetUsersListUseCase(private val userDetailsRepository: UserDetailsRepository) { 11 | suspend fun execute(): Flow>> { 12 | return userDetailsRepository.getUsersList() 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/LanguageIdentifierUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.TranslateRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class LanguageIdentifierUseCase(private val translateRepository: TranslateRepository) { 10 | suspend fun execute(text: String): Flow> { 11 | return translateRepository.languageIdentifier(text = text) 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/ListenFromToUserMessagesUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.FromToUser 4 | import com.example.dispatch.domain.models.Message 5 | import com.example.dispatch.domain.models.Response 6 | import com.example.dispatch.domain.repository.MessageRepository 7 | import kotlinx.coroutines.ExperimentalCoroutinesApi 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | @ExperimentalCoroutinesApi 11 | class ListenFromToUserMessagesUseCase(private val messageRepository: MessageRepository) { 12 | suspend fun execute(fromToUser: FromToUser): Flow> { 13 | return messageRepository.listenFromToUserMessages(fromToUser = fromToUser) 14 | } 15 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/RestoreUserByEmailUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserAuthRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class RestoreUserByEmailUseCase(private val userAuthRepository: UserAuthRepository) { 10 | suspend fun execute(email: String): Flow> { 11 | return userAuthRepository.restorePasswordByEmail(email = email) 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/SaveLatestMessageUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Message 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.repository.MessageRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class SaveLatestMessageUseCase(private val messageRepository: MessageRepository) { 11 | suspend fun execute(message: Message): Flow> { 12 | return messageRepository.saveLatestMessage(message = message) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/SaveMessageUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Message 4 | import com.example.dispatch.domain.models.Response 5 | import com.example.dispatch.domain.repository.MessageRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class SaveMessageUseCase(private val messageRepository: MessageRepository) { 11 | suspend fun execute(message: Message): Flow> { 12 | return messageRepository.save(message = message) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/SaveUserDetailsUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserDetails 5 | import com.example.dispatch.domain.repository.UserDetailsRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class SaveUserDetailsUseCase(private val userDetailsRepository: UserDetailsRepository) { 11 | suspend fun execute(userDetails: UserDetails): Flow> { 12 | return userDetailsRepository.save(userDetails = userDetails) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/SaveUserImageProfileUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserImagesRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class SaveUserImageProfileUseCase(private val userImagesRepository: UserImagesRepository) { 10 | suspend fun execute(newImageUriStr: String): Flow> { 11 | return userImagesRepository.saveImageProfile(newImageUriStr = newImageUriStr) 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/SignInUserAuthUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserAuth 5 | import com.example.dispatch.domain.repository.UserAuthRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class SignInUserAuthUseCase(private val userAuthRepository: UserAuthRepository) { 11 | suspend fun execute(userAuth: UserAuth): Flow> { 12 | return userAuthRepository.login(userAuth = userAuth) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/SignOutUserAuthUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.UserAuthRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class SignOutUserAuthUseCase(private val userAuthRepository: UserAuthRepository) { 10 | suspend fun execute(): Flow> { 11 | return userAuthRepository.signOut() 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/SignUpUserAuthUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.models.UserAuth 5 | import com.example.dispatch.domain.repository.UserAuthRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @ExperimentalCoroutinesApi 10 | class SignUpUserAuthUseCase(private val userAuthRepository: UserAuthRepository) { 11 | suspend fun execute(userAuth: UserAuth): Flow> { 12 | return userAuthRepository.register(userAuth = userAuth) 13 | } 14 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/TranslateEnglishRussianTextUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.TranslateRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class TranslateEnglishRussianTextUseCase(private val translateRepository: TranslateRepository) { 10 | suspend fun execute(text: String): Flow> { 11 | return translateRepository.translateEnglishRussianText(text = text) 12 | } 13 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/example/dispatch/domain/usecase/TranslateRussianEnglishTextUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.example.dispatch.domain.usecase 2 | 3 | import com.example.dispatch.domain.models.Response 4 | import com.example.dispatch.domain.repository.TranslateRepository 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.flow.Flow 7 | 8 | @ExperimentalCoroutinesApi 9 | class TranslateRussianEnglishTextUseCase(private val translateRepository: TranslateRepository) { 10 | suspend fun execute(text: String): Flow> { 11 | return translateRepository.translateRussianEnglishText(text = text) 12 | } 13 | } -------------------------------------------------------------------------------- /github_images/example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/github_images/example1.png -------------------------------------------------------------------------------- /github_images/example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/github_images/example2.png -------------------------------------------------------------------------------- /github_images/example3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/github_images/example3.png -------------------------------------------------------------------------------- /github_images/example4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/github_images/example4.png -------------------------------------------------------------------------------- /github_images/example5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/github_images/example5.png -------------------------------------------------------------------------------- /github_images/example6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/github_images/example6.png -------------------------------------------------------------------------------- /github_images/example7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/github_images/example7.png -------------------------------------------------------------------------------- /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 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphanication/Dispatch/62c10da5f7c74ace4bc2e7de927c312a6611596b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Apr 14 14:26:07 MSK 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | maven { url "https://maven.google.com" } 5 | google() 6 | mavenCentral() 7 | maven { url "https://jitpack.io"} 8 | jcenter() 9 | } 10 | } 11 | dependencyResolutionManagement { 12 | repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) 13 | repositories { 14 | maven { url "https://maven.google.com" } 15 | google() 16 | mavenCentral() 17 | maven { url "https://jitpack.io"} 18 | jcenter() 19 | } 20 | } 21 | rootProject.name = "Dispatch" 22 | include ':app' 23 | include ':domain' 24 | include ':data' 25 | --------------------------------------------------------------------------------