diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0288a6f..4982c60 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,10 +2,10 @@ name: Android CI/CD on: pull_request: branches: [ master ] - - push: + + push: branches: [ master ] - + workflow_dispatch: jobs: @@ -23,7 +23,8 @@ jobs: GOOGLE_SERVICE: ${{ secrets.GOOGLE_SERVICE }} run: | echo "$GOOGLE_SERVICE" >> app/google-services.json - + echo "$GOOGLE_SERVICE" >> auth/google-services.json + - name: Set up JDK 11 uses: actions/setup-java@v3 with: @@ -45,6 +46,6 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle-. - + - name: Build application run: ./gradlew build --full-stacktrace diff --git a/.github/workflows/emulator.yml b/.github/workflows/emulator.yml index 79fc236..f5e1d11 100644 --- a/.github/workflows/emulator.yml +++ b/.github/workflows/emulator.yml @@ -22,6 +22,7 @@ jobs: GOOGLE_SERVICE: ${{ secrets.GOOGLE_SERVICE }} run: | echo "$GOOGLE_SERVICE" >> app/google-services.json + echo "$GOOGLE_SERVICE" >> auth/google-services.json - name: Set up JDK 11 uses: actions/setup-java@v3 diff --git a/.idea/compiler.xml b/.idea/compiler.xml index d4f7c5e..fb7f4a8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,12 +1,6 @@ - - - - - - - + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 8fa2b66..b8c125a 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -7,11 +7,11 @@ - + - + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 0c3054c..3571997 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -16,10 +16,10 @@ - diff --git a/.idea/misc.xml b/.idea/misc.xml index 71a5451..7bb2e21 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -28,6 +28,8 @@ + + @@ -37,11 +39,21 @@ - - + + + + + + + + + + + + @@ -63,7 +75,7 @@ - + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5565d74..150427e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,9 +1,6 @@ plugins { id("com.android.application") id("kotlin-android") - id("kotlin-kapt") - id("kotlin-parcelize") - id("androidx.navigation.safeargs.kotlin") id("com.google.gms.google-services") } @@ -27,19 +24,6 @@ android { "proguard-rules.pro" ) } - getByName("debug") { - isTestCoverageEnabled = true - } - } - - testOptions { - unitTests { - isIncludeAndroidResources = true - } - } - - viewBinding { - android.buildFeatures.viewBinding = true } compileOptions { @@ -60,65 +44,5 @@ dependencies { implementation(project(":splashscreen")) implementation(project(":main")) implementation(project(":auth")) - - implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.21") - - /*Serialization*/ - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") - - /*Paging*/ - implementation("androidx.paging:paging-runtime-ktx:3.1.1") - - /*Dagger 2*/ - val daggerVersion = "2.41" - implementation("com.google.dagger:dagger:$daggerVersion") - implementation("com.google.dagger:dagger-android:$daggerVersion") - implementation("com.google.dagger:dagger-android-support:$daggerVersion") - kapt("com.google.dagger:dagger-compiler:$daggerVersion") - kapt("com.google.dagger:dagger-android-processor:$daggerVersion") - - /*Firebase*/ - implementation(platform("com.google.firebase:firebase-bom:28.4.0")) - implementation("com.google.firebase:firebase-auth:21.0.3") - implementation("com.google.firebase:firebase-auth-ktx") - implementation("com.google.firebase:firebase-database:20.0.4") - implementation("com.google.android.gms:play-services-gcm:17.0.0") - kapt("com.google.firebase:firebase-auth:21.0.3") - implementation("com.google.firebase:firebase-storage:20.0.1") - implementation("com.firebaseui:firebase-ui-database:8.0.1") - - /*Glide*/ - val glideVersion = "4.12.0" - implementation("com.github.bumptech.glide:glide:$glideVersion") - annotationProcessor("com.github.bumptech.glide:compiler:$glideVersion") - - /*Drawer Layout*/ - implementation("androidx.drawerlayout:drawerlayout:1.1.1") - - /*Navigation Component*/ - val navVersion = "2.4.2" - implementation("androidx.navigation:navigation-fragment-ktx:$navVersion") - implementation("androidx.navigation:navigation-ui-ktx:$navVersion") - androidTestImplementation("androidx.navigation:navigation-testing:$navVersion") - - /*Lifecycle components*/ - val lifecycleVersion = "2.4.1" - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion") - implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion") - implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion") - - /*Coroutines*/ - val coroutinesVersion = "1.6.1-native-mt" - api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") - api("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion") - - //noinspection DifferentStdlibGradleVersion - implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21") - implementation("androidx.core:core-ktx:1.7.0") - implementation("androidx.appcompat:appcompat:1.4.1") - implementation("com.google.android.material:material:1.7.0-alpha01") - implementation("androidx.constraintlayout:constraintlayout:2.1.3") - testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.3") - androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") + implementation(project(":resources")) } \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index d99b33c..975872b 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle.kts.kts. +# proguardFiles setting in build.gradle.kts.kts.kts.kts. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c3edcce..8210788 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,17 +6,16 @@ + android:theme="@style/NewProjectTheme"> + android:theme="@style/NewSplashTheme"> @@ -24,10 +23,11 @@ + android:exported="false" /> + android:exported="false" + android:windowSoftInputMode="stateAlwaysVisible" /> appComponent - else -> (applicationContext as MessengerApplication).appComponent - } \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml deleted file mode 100644 index 968597c..0000000 --- a/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml deleted file mode 100644 index 9ebf9b4..0000000 --- a/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/auth/build.gradle.kts b/auth/build.gradle.kts index 86b2a1f..1949d21 100644 --- a/auth/build.gradle.kts +++ b/auth/build.gradle.kts @@ -3,7 +3,6 @@ plugins { id("kotlin-android") id("kotlin-kapt") id("androidx.navigation.safeargs.kotlin") - id("com.google.gms.google-services") } android { @@ -55,6 +54,7 @@ android { dependencies { implementation(project(":phoneedittext")) implementation(project(":core")) + implementation(project(":resources")) /*Navigation Component*/ val navVersion = "2.4.2" @@ -71,28 +71,19 @@ dependencies { /*Firebase*/ implementation(platform("com.google.firebase:firebase-bom:28.4.0")) - implementation("com.google.firebase:firebase-messaging-ktx:23.0.3") + implementation("com.google.firebase:firebase-messaging-ktx:23.0.4") implementation("com.google.firebase:firebase-common-ktx") implementation("com.google.firebase:firebase-auth-ktx") implementation("com.google.firebase:firebase-database-ktx") implementation("com.google.firebase:firebase-storage-ktx") implementation("com.google.firebase:firebase-analytics-ktx") - - /*Lifecycle components*/ - val lifecycleVersion = "2.4.1" - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion") - implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion") - implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion") - /*Coroutines*/ - val coroutineVersion = "1.6.1-native-mt" - api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion") - api("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion") + api("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1") implementation("androidx.core:core-ktx:1.7.0") implementation("androidx.appcompat:appcompat:1.4.1") - implementation("com.google.android.material:material:1.5.0") + implementation("com.google.android.material:material:1.6.0") implementation("androidx.constraintlayout:constraintlayout:2.1.3") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.3") diff --git a/auth/proguard-rules.pro b/auth/proguard-rules.pro index ff59496..d99b33c 100644 --- a/auth/proguard-rules.pro +++ b/auth/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle.kts. +# proguardFiles setting in build.gradle.kts.kts. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/auth/src/main/java/st/slex/messenger/auth/data/model/AuthModel.kt b/auth/src/main/java/st/slex/messenger/auth/data/model/AuthModel.kt deleted file mode 100644 index ed3e604..0000000 --- a/auth/src/main/java/st/slex/messenger/auth/data/model/AuthModel.kt +++ /dev/null @@ -1,16 +0,0 @@ -package st.slex.messenger.auth.data.model - -interface AuthModel { - - fun id(): String - fun phone(): String - - data class Base( - private val id: String, - private val phone: String - ) : AuthModel { - - override fun id(): String = id - override fun phone(): String = phone - } -} \ No newline at end of file diff --git a/auth/src/main/java/st/slex/messenger/auth/di/AuthComponent.kt b/auth/src/main/java/st/slex/messenger/auth/di/AuthComponent.kt index 041c2d9..fe69d0a 100644 --- a/auth/src/main/java/st/slex/messenger/auth/di/AuthComponent.kt +++ b/auth/src/main/java/st/slex/messenger/auth/di/AuthComponent.kt @@ -3,12 +3,10 @@ package st.slex.messenger.auth.di import dagger.BindsInstance import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi -import st.slex.messenger.auth.di.scopes.AuthScope import st.slex.messenger.auth.ui.AuthActivity import st.slex.messenger.auth.ui.EnterCodeFragment import st.slex.messenger.auth.ui.EnterPhoneFragment -@AuthScope @Component(modules = [AuthModule::class]) interface AuthComponent { diff --git a/auth/src/main/java/st/slex/messenger/auth/di/modules/UtilsModule.kt b/auth/src/main/java/st/slex/messenger/auth/di/modules/UtilsModule.kt index 1f902c1..ce40c1e 100644 --- a/auth/src/main/java/st/slex/messenger/auth/di/modules/UtilsModule.kt +++ b/auth/src/main/java/st/slex/messenger/auth/di/modules/UtilsModule.kt @@ -4,18 +4,19 @@ import dagger.Binds import dagger.Module import st.slex.messenger.auth.data.utils.interf.TokenUtil import st.slex.messenger.auth.data.utils.real.TokenUtilImpl -import st.slex.messenger.auth.ui.SendCodeEngine -import st.slex.messenger.auth.ui.utils.LoginHelper -import st.slex.messenger.auth.ui.utils.LoginHelperImpl +import st.slex.messenger.auth.ui.use_case.impl.LoginUseCaseImpl +import st.slex.messenger.auth.ui.use_case.impl.SendCodeUseCaseImpl +import st.slex.messenger.auth.ui.use_case.interf.LoginUseCase +import st.slex.messenger.auth.ui.use_case.interf.SendCodeUseCase @Module interface UtilsModule { @Binds - fun bindsLoginHelper(engine: LoginHelperImpl): LoginHelper + fun bindsLoginHelper(engine: LoginUseCaseImpl): LoginUseCase @Binds - fun bindsSendCodeEngine(engine: SendCodeEngine.Base): SendCodeEngine + fun bindsSendCodeEngine(engine: SendCodeUseCaseImpl): SendCodeUseCase @Binds fun bindsTokenUtilHelper(util: TokenUtilImpl): TokenUtil diff --git a/auth/src/main/java/st/slex/messenger/auth/di/modules/ViewModelFactoryModule.kt b/auth/src/main/java/st/slex/messenger/auth/di/modules/ViewModelFactoryModule.kt index ff4e9ca..3b89dd1 100644 --- a/auth/src/main/java/st/slex/messenger/auth/di/modules/ViewModelFactoryModule.kt +++ b/auth/src/main/java/st/slex/messenger/auth/di/modules/ViewModelFactoryModule.kt @@ -3,7 +3,7 @@ package st.slex.messenger.auth.di.modules import androidx.lifecycle.ViewModelProvider import dagger.Binds import dagger.Module -import st.slex.messenger.auth.ui.ViewModelFactory +import st.slex.messenger.auth.ui.utils.ViewModelFactory @Module interface ViewModelFactoryModule { diff --git a/auth/src/main/java/st/slex/messenger/auth/di/scopes/AuthScope.kt b/auth/src/main/java/st/slex/messenger/auth/di/scopes/AuthScope.kt deleted file mode 100644 index ec94203..0000000 --- a/auth/src/main/java/st/slex/messenger/auth/di/scopes/AuthScope.kt +++ /dev/null @@ -1,7 +0,0 @@ -package st.slex.messenger.auth.di.scopes - -import javax.inject.Scope - -@Scope -@Retention(value = AnnotationRetention.RUNTIME) -annotation class AuthScope \ No newline at end of file diff --git a/auth/src/main/java/st/slex/messenger/auth/domain/interf/LoginDomainMapper.kt b/auth/src/main/java/st/slex/messenger/auth/domain/interf/LoginDomainMapper.kt index 8fd31ec..8876de2 100644 --- a/auth/src/main/java/st/slex/messenger/auth/domain/interf/LoginDomainMapper.kt +++ b/auth/src/main/java/st/slex/messenger/auth/domain/interf/LoginDomainMapper.kt @@ -1,7 +1,7 @@ package st.slex.messenger.auth.domain.interf import st.slex.messenger.auth.core.LoginValue -import st.slex.messenger.auth.ui.LoginUIResult +import st.slex.messenger.auth.ui.core.LoginUIResult interface LoginDomainMapper { fun map(data: LoginValue): LoginUIResult diff --git a/auth/src/main/java/st/slex/messenger/auth/domain/real/AuthInteractorImpl.kt b/auth/src/main/java/st/slex/messenger/auth/domain/real/AuthInteractorImpl.kt index 7005545..93b5cca 100644 --- a/auth/src/main/java/st/slex/messenger/auth/domain/real/AuthInteractorImpl.kt +++ b/auth/src/main/java/st/slex/messenger/auth/domain/real/AuthInteractorImpl.kt @@ -9,15 +9,15 @@ import st.slex.core.Resource import st.slex.messenger.auth.core.LoginValue import st.slex.messenger.auth.domain.interf.AuthInteractor import st.slex.messenger.auth.domain.interf.AuthRepository -import st.slex.messenger.auth.ui.SendCodeEngine -import st.slex.messenger.auth.ui.utils.LoginHelper +import st.slex.messenger.auth.ui.use_case.interf.LoginUseCase +import st.slex.messenger.auth.ui.use_case.interf.SendCodeUseCase import javax.inject.Inject @ExperimentalCoroutinesApi class AuthInteractorImpl @Inject constructor( private val repository: AuthRepository, - private val loginEngine: LoginHelper, - private val sendCodeEngine: SendCodeEngine, + private val loginEngine: LoginUseCase, + private val sendCodeEngine: SendCodeUseCase, ) : AuthInteractor { override suspend fun login(phone: String): Flow = flow { diff --git a/auth/src/main/java/st/slex/messenger/auth/domain/real/LoginDomainMapperImpl.kt b/auth/src/main/java/st/slex/messenger/auth/domain/real/LoginDomainMapperImpl.kt index 1ad8cee..595e66d 100644 --- a/auth/src/main/java/st/slex/messenger/auth/domain/real/LoginDomainMapperImpl.kt +++ b/auth/src/main/java/st/slex/messenger/auth/domain/real/LoginDomainMapperImpl.kt @@ -2,7 +2,7 @@ package st.slex.messenger.auth.domain.real import st.slex.messenger.auth.core.LoginValue import st.slex.messenger.auth.domain.interf.LoginDomainMapper -import st.slex.messenger.auth.ui.LoginUIResult +import st.slex.messenger.auth.ui.core.LoginUIResult import javax.inject.Inject class LoginDomainMapperImpl @Inject constructor() : LoginDomainMapper { diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/AuthViewModel.kt b/auth/src/main/java/st/slex/messenger/auth/ui/AuthViewModel.kt index 919e0f4..8204ff3 100644 --- a/auth/src/main/java/st/slex/messenger/auth/ui/AuthViewModel.kt +++ b/auth/src/main/java/st/slex/messenger/auth/ui/AuthViewModel.kt @@ -2,10 +2,12 @@ package st.slex.messenger.auth.ui import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* import st.slex.messenger.auth.domain.interf.AuthInteractor import st.slex.messenger.auth.domain.interf.LoginDomainMapper +import st.slex.messenger.auth.ui.core.LoginUIResult import javax.inject.Inject @ExperimentalCoroutinesApi @@ -14,21 +16,24 @@ class AuthViewModel @Inject constructor( private val mapper: LoginDomainMapper ) : ViewModel() { - suspend fun login(phone: String): StateFlow = - interactor.login(phone) - .flatMapLatest { flowOf(mapper.map(it)) } - .stateIn( - viewModelScope, - started = SharingStarted.Lazily, - initialValue = LoginUIResult.Loading - ) + suspend fun login(phone: String): StateFlow = interactor.login(phone) + .flatMapLatest { flowOf(mapper.map(it)) } + .flowOn(Dispatchers.IO) + .stateIn( + viewModelScope, + started = SharingStarted.Lazily, + initialValue = LoginUIResult.Loading + ) - suspend fun sendCode(id: String, code: String): Flow = - interactor.sendCode(id, code) - .flatMapLatest { flowOf(mapper.map(it)) } - .stateIn( - viewModelScope, - started = SharingStarted.Lazily, - initialValue = LoginUIResult.Loading - ) + suspend fun sendCode( + id: String, + code: String + ): StateFlow = interactor.sendCode(id, code) + .flatMapLatest { flowOf(mapper.map(it)) } + .flowOn(Dispatchers.IO) + .stateIn( + viewModelScope, + started = SharingStarted.Lazily, + initialValue = LoginUIResult.Loading + ) } \ No newline at end of file diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/EnterCodeFragment.kt b/auth/src/main/java/st/slex/messenger/auth/ui/EnterCodeFragment.kt index 706a22a..8226051 100644 --- a/auth/src/main/java/st/slex/messenger/auth/ui/EnterCodeFragment.kt +++ b/auth/src/main/java/st/slex/messenger/auth/ui/EnterCodeFragment.kt @@ -4,10 +4,12 @@ import android.content.Context import android.content.Intent import android.graphics.Color import android.os.Bundle +import android.util.Log +import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.EditText +import android.widget.FrameLayout import androidx.appcompat.app.AppCompatActivity import androidx.core.widget.addTextChangedListener import androidx.fragment.app.Fragment @@ -18,14 +20,15 @@ import androidx.navigation.fragment.navArgs import com.google.android.material.snackbar.Snackbar import com.google.android.material.transition.MaterialContainerTransform import dagger.Lazy -import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job import kotlinx.coroutines.launch import st.slex.messenger.auth.R import st.slex.messenger.auth.databinding.FragmentEnterCodeBinding +import st.slex.messenger.auth.ui.core.LoginUIResult import javax.inject.Inject -import kotlin.coroutines.suspendCoroutine + @ExperimentalCoroutinesApi class EnterCodeFragment : Fragment() { @@ -40,6 +43,9 @@ class EnterCodeFragment : Fragment() { viewModelFactory.get() } + private var sendCodeJob: Job = Job() + private var collectorJob: Job = Job() + @Inject fun injection(viewModelFactory: Lazy) { this.viewModelFactory = viewModelFactory @@ -72,81 +78,80 @@ class EnterCodeFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - editTextList.first().requestFocus() - viewLifecycleOwner.lifecycleScope.launchWhenResumed { - foreachCodeList(editTextList) { sendCode.start() } - } - } - - private val sendCode by lazy { - viewLifecycleOwner.lifecycleScope.launch( - context = Dispatchers.IO, - start = CoroutineStart.LAZY - ) { - viewModel.sendCode(id = args.id, code = codeFromList).collect(::collector) + binding.editText.requestFocus() + binding.editText.addTextChangedListener { + if (it?.length == 6) sendCode() } } - private val editTextList: List by lazy { - listOf( - binding.codeEditText1, binding.codeEditText2, binding.codeEditText3, - binding.codeEditText4, binding.codeEditText5, binding.codeEditText6 - ) - } - - private suspend inline fun foreachCodeList( - list: List, - crossinline startSendingCode: () -> Unit - ) = list.indices.forEach { - listenCode(list[it]).also { _ -> - if (it == list.size - 1) startSendingCode() - else list[it + 1].requestFocus() + private fun sendCode() { + sendCodeJob.cancel() + sendCodeJob = viewLifecycleOwner.lifecycleScope.launch(context = Dispatchers.IO) { + viewModel.sendCode( + id = args.id, + code = binding.editText.text.toString() + ).collect(::collector) } } - private suspend fun listenCode(editText: EditText): Unit = suspendCoroutine { continuation -> - editText.addTextChangedListener { - if (it?.length == 1) continuation.resumeWith(Result.success(Unit)) + private fun collector(resource: LoginUIResult) { + collectorJob.cancel() + collectorJob = viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { + when (resource) { + is LoginUIResult.Success -> success() + is LoginUIResult.Failure -> failure(resource.exception) + is LoginUIResult.Loading -> showProgress() + } } } - private val codeFromList: String - get() = editTextList.joinToString { it.text.toString() }.replace(", ", "") - - private fun collector( - resource: LoginUIResult - ) = viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { - when (resource) { - is LoginUIResult.Success -> success() - is LoginUIResult.Failure -> stopProgress() - is LoginUIResult.Loading -> showProgress() - } + private fun failure(exception: Exception) { + binding.editText.setText("") + stopProgress() + showError(exception) } private fun success() { stopProgress() val intent = Intent() intent.setClassName(requireContext(), MAIN_ACTIVITY_PATH) + Snackbar.make(binding.root, "SUCCESS", Snackbar.LENGTH_LONG).show() startActivity(intent) requireActivity().finish() - Snackbar.make(binding.root, "SUCCESS", Snackbar.LENGTH_LONG).show() - } - - private fun showProgress() { - binding.fragmentCodeProgressIndicator.visibility = View.VISIBLE } private fun stopProgress() { binding.fragmentCodeProgressIndicator.visibility = View.GONE } + private fun showProgress() { + binding.fragmentCodeProgressIndicator.visibility = View.VISIBLE + } + override fun onDestroyView() { super.onDestroyView() + sendCodeJob.cancel() + collectorJob.cancel() _binding = null } + private fun showError(throwable: Throwable) { + if (isVisible) { + Snackbar.make( + binding.root, + throwable.message.toString(), + Snackbar.LENGTH_SHORT + ).apply { + setAction("OK") {} + (view.layoutParams as FrameLayout.LayoutParams).gravity = Gravity.TOP + }.show() + } + Log.e(TAG, throwable.stackTraceToString()) + } + companion object { private const val MAIN_ACTIVITY_PATH = "st.slex.messenger.main.ui.MainActivity" + private const val TAG = "EnterCodeFragment" } } diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/EnterPhoneFragment.kt b/auth/src/main/java/st/slex/messenger/auth/ui/EnterPhoneFragment.kt index 5b23064..2dc6e95 100644 --- a/auth/src/main/java/st/slex/messenger/auth/ui/EnterPhoneFragment.kt +++ b/auth/src/main/java/st/slex/messenger/auth/ui/EnterPhoneFragment.kt @@ -1,13 +1,14 @@ package st.slex.messenger.auth.ui +import android.app.Activity import android.content.Context -import android.content.Context.INPUT_METHOD_SERVICE import android.content.Intent +import android.graphics.Color import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.inputmethod.InputMethodManager import androidx.appcompat.app.AppCompatActivity import androidx.core.widget.addTextChangedListener import androidx.fragment.app.Fragment @@ -16,13 +17,16 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.findNavController -import com.google.android.material.textfield.TextInputEditText -import dagger.Lazy +import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.launch import st.slex.messenger.auth.databinding.FragmentEnterPhoneBinding +import st.slex.messenger.auth.ui.core.LoginUIResult +import st.slex.resources.KeyboardUtil +import st.slex.resources.KeyboardUtilImpl +import java.lang.ref.WeakReference import java.util.* import javax.inject.Inject @@ -32,12 +36,17 @@ class EnterPhoneFragment : Fragment() { private var _binding: FragmentEnterPhoneBinding? = null private val binding get() = checkNotNull(_binding) - private lateinit var viewModelFactory: Lazy - private val viewModel: AuthViewModel by viewModels { viewModelFactory.get() } + private lateinit var viewModelFactory: ViewModelProvider.Factory + private val viewModel: AuthViewModel by viewModels { viewModelFactory } private var phoneClickJob: Job = Job() + private var collectorJob: Job = Job() + + private var _weakReference: WeakReference? = null + private val weakReference: WeakReference + get() = checkNotNull(_weakReference) @Inject - fun injection(viewModelFactory: Lazy) { + fun injection(viewModelFactory: ViewModelProvider.Factory) { this.viewModelFactory = viewModelFactory } @@ -52,58 +61,63 @@ class EnterPhoneFragment : Fragment() { savedInstanceState: Bundle? ): View { _binding = FragmentEnterPhoneBinding.inflate(inflater, container, false) - (activity as AppCompatActivity).supportActionBar?.title = "Phone Number" + (activity as AppCompatActivity).supportActionBar?.title = FRAGMENT_TITLE + _weakReference = WeakReference(requireActivity()) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - showKeyboard(binding.phoneEditText) + val keyboardUtil: KeyboardUtil = KeyboardUtilImpl(weakReference) + binding.phoneEditText.requestFocus() binding.phoneEditText.setRegionCode(Locale.getDefault().country) binding.phoneEditText.addTextChangedListener { - binding.fragmentPhoneFab.isEnabled = - binding.phoneEditText.isTextValidInternationalPhoneNumber() + if (binding.phoneEditText.isTextValidInternationalPhoneNumber()) { + binding.fragmentPhoneFab.isEnabled = true + keyboardUtil.hideKeyboard() + } else { + binding.fragmentPhoneFab.isEnabled = false + } } binding.fragmentPhoneFab.setOnClickListener(phoneClickListener) } private val phoneClickListener = View.OnClickListener { phoneClickJob.cancel() - val phone = binding.phoneEditText.text.toString() phoneClickJob = requireActivity().lifecycleScope.launch(Dispatchers.IO) { + val phone = binding.phoneEditText.text.toString() viewModel.login(phone).collect(::collector) } } - private fun showKeyboard(textInputEditText: TextInputEditText) { - textInputEditText.requestFocus() - val inputMethodManager = - requireActivity().getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - inputMethodManager.showSoftInput(textInputEditText, InputMethodManager.SHOW_IMPLICIT) - } - private fun collector(resource: LoginUIResult) { - viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { + collectorJob.cancel() + collectorJob = viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { when (resource) { is LoginUIResult.Success.LogIn -> resultLogIn() - is LoginUIResult.Success.SendCode -> resource.resultSendCode() - is LoginUIResult.Failure -> stopProgress() + is LoginUIResult.Success.SendCode -> resultSendCode(resource.id) + is LoginUIResult.Failure -> failureResult(resource.exception) is LoginUIResult.Loading -> showProgress() } } } + private fun failureResult(exception: Exception) { + stopProgress() + exception.cause?.let(::showError) + } + private fun resultLogIn() { val intent = Intent() intent.setClassName(requireContext(), MAIN_ACTIVITY_PATH) startActivity(intent) } - private fun LoginUIResult.Success.SendCode.resultSendCode() { - binding.fragmentCodeProgressIndicator.visibility = View.GONE - val direction = EnterPhoneFragmentDirections.actionNavAuthPhoneToNavAuthCode(id) - val extras = - FragmentNavigatorExtras(binding.fragmentPhoneFab to binding.fragmentPhoneFab.transitionName) + private fun resultSendCode(id: String) { + stopProgress() + val direction = EnterPhoneFragmentDirections.actionNavCode(id) + val sharedElements = binding.fragmentPhoneFab to binding.fragmentPhoneFab.transitionName + val extras = FragmentNavigatorExtras(sharedElements) findNavController().navigate(direction, extras) } @@ -118,14 +132,33 @@ class EnterPhoneFragment : Fragment() { override fun onDestroyView() { super.onDestroyView() _binding = null + weakReference.clear() + _weakReference = null } override fun onDestroy() { super.onDestroy() phoneClickJob.cancel() + collectorJob.cancel() + } + + private fun showError(throwable: Throwable) { + if (isVisible) { + Snackbar.make( + requireView(), + throwable.message.toString(), + Snackbar.LENGTH_SHORT + ).apply { + setBackgroundTint(Color.CYAN) + setAction("OK") {} + }.show() + } + Log.e(TAG, throwable.stackTraceToString()) } companion object { private const val MAIN_ACTIVITY_PATH = "st.slex.messenger.main.ui.MainActivity" + private const val FRAGMENT_TITLE = "Phone number" + private const val TAG = "EnterPhoneFragment" } } diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/SendCodeEngine.kt b/auth/src/main/java/st/slex/messenger/auth/ui/SendCodeEngine.kt deleted file mode 100644 index ba606d3..0000000 --- a/auth/src/main/java/st/slex/messenger/auth/ui/SendCodeEngine.kt +++ /dev/null @@ -1,34 +0,0 @@ -package st.slex.messenger.auth.ui - -import com.google.android.gms.tasks.OnCompleteListener -import com.google.firebase.auth.AuthResult -import com.google.firebase.auth.PhoneAuthProvider -import com.google.firebase.auth.ktx.auth -import com.google.firebase.ktx.Firebase -import st.slex.messenger.auth.core.LoginValue -import javax.inject.Inject -import kotlin.coroutines.suspendCoroutine - -interface SendCodeEngine { - - suspend fun sendCode(id: String, code: String): LoginValue - - class Base @Inject constructor() : SendCodeEngine { - - override suspend fun sendCode(id: String, code: String): LoginValue = - suspendCoroutine { continuation -> - val credential = PhoneAuthProvider.getCredential(id, code) - val task = Firebase.auth.signInWithCredential(credential) - val listener = listener { continuation.resumeWith(Result.success(it)) } - task.addOnCompleteListener(listener) - } - - private inline fun listener( - crossinline function: (LoginValue) -> Unit - ) = OnCompleteListener { - val authResult = if (it.isSuccessful) LoginValue.Success.LogIn - else LoginValue.Failure(it.exception!!) - function(authResult) - } - } -} \ No newline at end of file diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/LoginUIResult.kt b/auth/src/main/java/st/slex/messenger/auth/ui/core/LoginUIResult.kt similarity index 87% rename from auth/src/main/java/st/slex/messenger/auth/ui/LoginUIResult.kt rename to auth/src/main/java/st/slex/messenger/auth/ui/core/LoginUIResult.kt index 8773bfb..22dd5b9 100644 --- a/auth/src/main/java/st/slex/messenger/auth/ui/LoginUIResult.kt +++ b/auth/src/main/java/st/slex/messenger/auth/ui/core/LoginUIResult.kt @@ -1,4 +1,4 @@ -package st.slex.messenger.auth.ui +package st.slex.messenger.auth.ui.core sealed class LoginUIResult { diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/utils/LoginHelperImpl.kt b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/impl/LoginUseCaseImpl.kt similarity index 86% rename from auth/src/main/java/st/slex/messenger/auth/ui/utils/LoginHelperImpl.kt rename to auth/src/main/java/st/slex/messenger/auth/ui/use_case/impl/LoginUseCaseImpl.kt index 7432218..6fd43f2 100644 --- a/auth/src/main/java/st/slex/messenger/auth/ui/utils/LoginHelperImpl.kt +++ b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/impl/LoginUseCaseImpl.kt @@ -1,4 +1,4 @@ -package st.slex.messenger.auth.ui.utils +package st.slex.messenger.auth.ui.use_case.impl import com.google.firebase.auth.PhoneAuthOptions import com.google.firebase.auth.PhoneAuthProvider @@ -6,13 +6,14 @@ import com.google.firebase.auth.ktx.auth import com.google.firebase.ktx.Firebase import st.slex.messenger.auth.core.LoginValue import st.slex.messenger.auth.ui.AuthActivity +import st.slex.messenger.auth.ui.use_case.interf.LoginUseCase import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.coroutines.suspendCoroutine -class LoginHelperImpl @Inject constructor( +class LoginUseCaseImpl @Inject constructor( private val activity: AuthActivity -) : LoginHelper { +) : LoginUseCase { override suspend fun login(phone: String): LoginValue = suspendCoroutine { continuation -> diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/utils/PhoneAuthCallback.kt b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/impl/PhoneAuthCallback.kt similarity index 95% rename from auth/src/main/java/st/slex/messenger/auth/ui/utils/PhoneAuthCallback.kt rename to auth/src/main/java/st/slex/messenger/auth/ui/use_case/impl/PhoneAuthCallback.kt index 5e8a829..6e8d21c 100644 --- a/auth/src/main/java/st/slex/messenger/auth/ui/utils/PhoneAuthCallback.kt +++ b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/impl/PhoneAuthCallback.kt @@ -1,4 +1,4 @@ -package st.slex.messenger.auth.ui.utils +package st.slex.messenger.auth.ui.use_case.impl import com.google.firebase.FirebaseException import com.google.firebase.auth.PhoneAuthCredential diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/use_case/impl/SendCodeUseCaseImpl.kt b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/impl/SendCodeUseCaseImpl.kt new file mode 100644 index 0000000..66c6e94 --- /dev/null +++ b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/impl/SendCodeUseCaseImpl.kt @@ -0,0 +1,30 @@ +package st.slex.messenger.auth.ui.use_case.impl + +import com.google.android.gms.tasks.OnCompleteListener +import com.google.firebase.auth.AuthResult +import com.google.firebase.auth.PhoneAuthProvider +import com.google.firebase.auth.ktx.auth +import com.google.firebase.ktx.Firebase +import st.slex.messenger.auth.core.LoginValue +import st.slex.messenger.auth.ui.use_case.interf.SendCodeUseCase +import javax.inject.Inject +import kotlin.coroutines.suspendCoroutine + +class SendCodeUseCaseImpl @Inject constructor() : SendCodeUseCase { + + override suspend fun sendCode(id: String, code: String): LoginValue = + suspendCoroutine { continuation -> + val credential = PhoneAuthProvider.getCredential(id, code) + val task = Firebase.auth.signInWithCredential(credential) + val listener = listener { continuation.resumeWith(Result.success(it)) } + task.addOnCompleteListener(listener) + } + + private inline fun listener( + crossinline function: (LoginValue) -> Unit + ) = OnCompleteListener { + val authResult = if (it.isSuccessful) LoginValue.Success.LogIn + else LoginValue.Failure(it.exception!!) + function(authResult) + } +} \ No newline at end of file diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/utils/LoginHelper.kt b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/interf/LoginUseCase.kt similarity index 56% rename from auth/src/main/java/st/slex/messenger/auth/ui/utils/LoginHelper.kt rename to auth/src/main/java/st/slex/messenger/auth/ui/use_case/interf/LoginUseCase.kt index 4a06c7f..03e0034 100644 --- a/auth/src/main/java/st/slex/messenger/auth/ui/utils/LoginHelper.kt +++ b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/interf/LoginUseCase.kt @@ -1,7 +1,7 @@ -package st.slex.messenger.auth.ui.utils +package st.slex.messenger.auth.ui.use_case.interf import st.slex.messenger.auth.core.LoginValue -interface LoginHelper { +interface LoginUseCase { suspend fun login(phone: String): LoginValue } \ No newline at end of file diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/use_case/interf/SendCodeUseCase.kt b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/interf/SendCodeUseCase.kt new file mode 100644 index 0000000..9bb1a07 --- /dev/null +++ b/auth/src/main/java/st/slex/messenger/auth/ui/use_case/interf/SendCodeUseCase.kt @@ -0,0 +1,7 @@ +package st.slex.messenger.auth.ui.use_case.interf + +import st.slex.messenger.auth.core.LoginValue + +interface SendCodeUseCase { + suspend fun sendCode(id: String, code: String): LoginValue +} \ No newline at end of file diff --git a/auth/src/main/java/st/slex/messenger/auth/ui/ViewModelFactory.kt b/auth/src/main/java/st/slex/messenger/auth/ui/utils/ViewModelFactory.kt similarity index 95% rename from auth/src/main/java/st/slex/messenger/auth/ui/ViewModelFactory.kt rename to auth/src/main/java/st/slex/messenger/auth/ui/utils/ViewModelFactory.kt index 3380316..1b6cb5b 100644 --- a/auth/src/main/java/st/slex/messenger/auth/ui/ViewModelFactory.kt +++ b/auth/src/main/java/st/slex/messenger/auth/ui/utils/ViewModelFactory.kt @@ -1,4 +1,4 @@ -package st.slex.messenger.auth.ui +package st.slex.messenger.auth.ui.utils import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider diff --git a/auth/src/main/res/layout/fragment_enter_code.xml b/auth/src/main/res/layout/fragment_enter_code.xml index f5b1656..b5ad288 100644 --- a/auth/src/main/res/layout/fragment_enter_code.xml +++ b/auth/src/main/res/layout/fragment_enter_code.xml @@ -5,70 +5,28 @@ android:layout_height="match_parent" tools:context=".ui.EnterCodeFragment"> - + android:hint="@string/code"> - - - - - - - - - - - - - - - - + android:inputType="number" + android:lines="1" + android:maxLength="6" + android:textSize="40sp" /> + - diff --git a/auth/src/main/res/layout/fragment_enter_phone.xml b/auth/src/main/res/layout/fragment_enter_phone.xml index e815473..d8e09af 100644 --- a/auth/src/main/res/layout/fragment_enter_phone.xml +++ b/auth/src/main/res/layout/fragment_enter_phone.xml @@ -8,7 +8,7 @@ + app:startDestination="@id/nav_phone"> + android:id="@+id/action_nav_code" + app:destination="@id/nav_code" /> + + + + \ No newline at end of file diff --git a/auth/src/main/res/values-night/themes.xml b/auth/src/main/res/values-night/themes.xml deleted file mode 100644 index db2d048..0000000 --- a/auth/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/auth/src/main/res/values/styles.xml b/auth/src/main/res/values/styles.xml new file mode 100644 index 0000000..821da98 --- /dev/null +++ b/auth/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/auth/src/main/res/values/themes.xml b/auth/src/main/res/values/themes.xml deleted file mode 100644 index 1ba5e45..0000000 --- a/auth/src/main/res/values/themes.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 5e7beb0..0000000 --- a/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -buildscript { - ext { - kotlin_version = "1.6.21" - nav_version = '2.4.2' - } - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:7.1.3' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21' - classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" - classpath 'com.google.gms:google-services:4.3.10' - } -} - -allprojects { - repositories { - google() - mavenCentral() - maven { url 'https://jitpack.io' } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..751e8de --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,28 @@ +import java.net.URI + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:7.1.3") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21") + classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.4.2") + classpath("com.google.gms:google-services:4.3.10") + } +} + +allprojects { + repositories { + google() + mavenCentral() + maven { + url = URI("https://jitpack.io") + } + } +} + +tasks.register(name = "type", type = Delete::class) { + delete(rootProject.buildDir) +} \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 0d31ad5..d182641 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -4,6 +4,6 @@ plugins { } java { - sourceCompatibility = JavaVersion.VERSION_1_7 - targetCompatibility = JavaVersion.VERSION_1_7 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } \ No newline at end of file diff --git a/main/build.gradle b/main/build.gradle deleted file mode 100644 index f67718b..0000000 --- a/main/build.gradle +++ /dev/null @@ -1,102 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' - id 'kotlin-kapt' - id 'kotlin-parcelize' - id 'androidx.navigation.safeargs.kotlin' - id 'com.google.gms.google-services' -} - -android { - compileSdk 31 - - defaultConfig { - minSdk 23 - targetSdk 31 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } - buildFeatures { - viewBinding true - } -} - -dependencies { - implementation(project(":core")) - implementation "org.jetbrains.kotlin:kotlin-reflect:1.5.31" - - /*Work Manager*/ - implementation "androidx.work:work-runtime-ktx:2.7.0" - - /*Serialization*/ - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0-RC") - - /*Paging*/ - implementation("androidx.paging:paging-runtime-ktx:3.0.1") - - /*Dagger 2*/ - implementation("com.google.dagger:dagger:2.39.1") - implementation("androidx.legacy:legacy-support-v4:1.0.0") - implementation("com.google.dagger:dagger-android:2.37") - implementation("com.google.dagger:dagger-android-support:2.37") - kapt("com.google.dagger:dagger-compiler:2.37") - kapt("com.google.dagger:dagger-android-processor:2.37") - - /*Firebase*/ - implementation platform('com.google.firebase:firebase-bom:28.4.0') - implementation('com.google.firebase:firebase-auth') - implementation('com.google.firebase:firebase-auth-ktx') - implementation('com.google.firebase:firebase-database') - implementation 'com.google.firebase:firebase-messaging-ktx' - implementation 'com.google.firebase:firebase-analytics-ktx' - implementation("com.google.firebase:firebase-storage") - implementation("com.firebaseui:firebase-ui-database:8.0.0") - implementation("com.google.android.gms:play-services-gcm:17.0.0") - kapt('com.google.firebase:firebase-auth:21.0.1') - - /*Glide*/ - implementation 'com.github.bumptech.glide:glide:4.11.0' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' - - /*Drawer Layout*/ - implementation "androidx.drawerlayout:drawerlayout:1.1.1" - - /*Navigation Component*/ - implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" - implementation "androidx.navigation:navigation-ui-ktx:$nav_version" - - /*Testing Navigation*/ - androidTestImplementation "androidx.navigation:navigation-testing:$nav_version" - - /*Lifecycle components*/ - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" - implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1" - - /*Coroutines*/ - api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' - api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2-native-mt' - - implementation 'androidx.core:core-ktx:1.6.0' - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'com.google.android.material:material:1.5.0-alpha04' - testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} \ No newline at end of file diff --git a/main/build.gradle.kts b/main/build.gradle.kts new file mode 100644 index 0000000..76d3ce2 --- /dev/null +++ b/main/build.gradle.kts @@ -0,0 +1,112 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-kapt") + id("kotlin-parcelize") + id("androidx.navigation.safeargs.kotlin") +} + +android { + compileSdk = 32 + + defaultConfig { + minSdk = 23 + targetSdk = 32 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + getByName("release") { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + getByName("debug") { + isTestCoverageEnabled = true + } + } + + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } + + viewBinding { + android.buildFeatures.viewBinding = true + } + + compileOptions { + sourceCompatibility(JavaVersion.VERSION_11) + targetCompatibility(JavaVersion.VERSION_11) + } + + sourceSets { + getByName("androidTest").assets.srcDir("$projectDir/schemas") + } + + kotlinOptions { + jvmTarget = "11" + } +} + +dependencies { + implementation(project(":core")) + implementation(project(":resources")) + + implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.21") + + /*Work Manager*/ + implementation("androidx.work:work-runtime-ktx:2.7.1") + + /*Serialization*/ + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + + /*Paging*/ + implementation("androidx.paging:paging-runtime-ktx:3.1.1") + + /*Dagger 2*/ + implementation("com.google.dagger:dagger:2.41") + implementation("com.google.dagger:dagger-android:2.41") + implementation("com.google.dagger:dagger-android-support:2.41") + kapt("com.google.dagger:dagger-compiler:2.41") + kapt("com.google.dagger:dagger-android-processor:2.41") + + /*Firebase*/ + implementation(platform("com.google.firebase:firebase-bom:28.4.0")) + implementation("com.google.firebase:firebase-auth") + implementation("com.google.firebase:firebase-auth-ktx") + implementation("com.google.firebase:firebase-database") + implementation("com.google.firebase:firebase-messaging-ktx") + implementation("com.google.firebase:firebase-analytics-ktx") + implementation("com.google.firebase:firebase-storage") + implementation("com.firebaseui:firebase-ui-database:8.0.1") + implementation("com.google.android.gms:play-services-gcm:17.0.0") + kapt("com.google.firebase:firebase-auth:21.0.3") + + /*Glide*/ + implementation("com.github.bumptech.glide:glide:4.13.1") + annotationProcessor("com.github.bumptech.glide:compiler:4.12.0") + + /*Drawer Layout*/ + implementation("androidx.drawerlayout:drawerlayout:1.1.1") + + /*Navigation Component*/ + val navVersion = "2.4.2" + implementation("androidx.navigation:navigation-fragment-ktx:$navVersion") + implementation("androidx.navigation:navigation-ui-ktx:$navVersion") + androidTestImplementation("androidx.navigation:navigation-testing:$navVersion") + + /*Coroutines*/ + api("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1-native-mt") + + implementation("androidx.core:core-ktx:1.7.0") + implementation("androidx.appcompat:appcompat:1.4.1") + implementation("com.google.android.material:material:1.7.0-alpha01") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.3") + androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") +} \ No newline at end of file diff --git a/main/proguard-rules.pro b/main/proguard-rules.pro index 481bb43..ff59496 100644 --- a/main/proguard-rules.pro +++ b/main/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. +# proguardFiles setting in build.gradle.kts. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/main/src/main/AndroidManifest.xml b/main/src/main/AndroidManifest.xml index a051063..097c4d7 100644 --- a/main/src/main/AndroidManifest.xml +++ b/main/src/main/AndroidManifest.xml @@ -1,4 +1,2 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/main/src/main/java/st/slex/messenger/main/ui/chats/ChatsFragment.kt b/main/src/main/java/st/slex/messenger/main/ui/chats/ChatsFragment.kt index 9fad573..d03096c 100644 --- a/main/src/main/java/st/slex/messenger/main/ui/chats/ChatsFragment.kt +++ b/main/src/main/java/st/slex/messenger/main/ui/chats/ChatsFragment.kt @@ -24,7 +24,6 @@ import st.slex.messenger.main.databinding.FragmentChatsBinding import st.slex.messenger.main.databinding.NavigationDrawerHeaderBinding import st.slex.messenger.main.ui.MainActivity import st.slex.messenger.main.ui.core.BaseFragment -import st.slex.messenger.main.ui.core.UIExtensions.changeVisibility import st.slex.messenger.main.ui.user_profile.UserUI import javax.inject.Inject @@ -121,7 +120,7 @@ class ChatsFragment : BaseFragment() { } } is Resource.Failure<*> -> result.failureResult(view) - is Resource.Loading -> view.loadingResult() + is Resource.Loading -> showLoading() } } @@ -136,13 +135,13 @@ class ChatsFragment : BaseFragment() { @JvmName("collectorUserUI") private suspend fun Resource.Success.successResult(view: View) { - view.changeVisibility() + hideLoading() drawResult(data) } @JvmName("collectorChatsUI") private suspend fun Resource.Success>>.successResult(view: View) { - view.changeVisibility() + hideLoading() drawResult(data) } @@ -160,11 +159,17 @@ class ChatsFragment : BaseFragment() { } private suspend fun Resource.Failure.failureResult(view: View) { - view.changeVisibility() + hideLoading() Log.e(TAG, exception.message, exception.cause) } - private suspend fun View.loadingResult() = changeVisibility() + private fun showLoading() { + binding.SHOWPROGRESS.show() + } + + private fun hideLoading() { + binding.SHOWPROGRESS.hide() + } override fun onDestroyView() { super.onDestroyView() diff --git a/main/src/main/java/st/slex/messenger/main/ui/core/ViewModelFactory.kt b/main/src/main/java/st/slex/messenger/main/ui/core/ViewModelFactory.kt index e5e01ec..68144ec 100644 --- a/main/src/main/java/st/slex/messenger/main/ui/core/ViewModelFactory.kt +++ b/main/src/main/java/st/slex/messenger/main/ui/core/ViewModelFactory.kt @@ -9,18 +9,17 @@ class ViewModelFactory @Inject constructor( private val viewModelMap: Map, @JvmSuppressWildcards Provider> ) : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - var viewModel = viewModelMap[modelClass] - if (viewModel == null) { - for (entry in viewModelMap) { - if (modelClass.isAssignableFrom(entry.key)) { - viewModel = entry.value - break - } - } - } - if (viewModel == null) throw IllegalArgumentException("Unknown model class $modelClass") - @Suppress("UNCHECKED_CAST") - return viewModel.get() as T - } + override fun create(modelClass: Class): T = viewModelMap[modelClass] + ?.let(::viewModelMapper) + ?: modelClass.filterViewModelMap?.let(::viewModelMapper) + ?: throw IllegalArgumentException("Unknown model class $modelClass") + + @Suppress("UNCHECKED_CAST") + private fun viewModelMapper(provider: Provider): T = + provider.get() as T + + private val Class.filterViewModelMap: Provider? + get() = viewModelMap.filter { + isAssignableFrom(it.key) + }.firstNotNullOfOrNull { it.value } } \ No newline at end of file diff --git a/main/src/main/java/st/slex/messenger/main/ui/single_chat/MessageUI.kt b/main/src/main/java/st/slex/messenger/main/ui/single_chat/MessageUI.kt index 8393732..e6d87ff 100644 --- a/main/src/main/java/st/slex/messenger/main/ui/single_chat/MessageUI.kt +++ b/main/src/main/java/st/slex/messenger/main/ui/single_chat/MessageUI.kt @@ -1,6 +1,7 @@ package st.slex.messenger.main.ui.single_chat import android.view.View +import android.view.ViewGroup import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import st.slex.messenger.main.utilites.funs.convertToTime @@ -8,8 +9,8 @@ import st.slex.messenger.main.utilites.funs.convertToTime interface MessageUI { fun bindMessage( - hideLayout: ConstraintLayout, - showLayout: ConstraintLayout, + hideLayout: ViewGroup, + showLayout: ViewGroup, messageTextView: TextView, timeStampTextView: TextView ) @@ -25,8 +26,8 @@ interface MessageUI { override fun getId(): String = from override fun bindMessage( - hideLayout: ConstraintLayout, - showLayout: ConstraintLayout, + hideLayout: ViewGroup, + showLayout: ViewGroup, messageTextView: TextView, timeStampTextView: TextView ) { diff --git a/main/src/main/java/st/slex/messenger/main/ui/single_chat/SingleChatFragment.kt b/main/src/main/java/st/slex/messenger/main/ui/single_chat/SingleChatFragment.kt index 195fce3..2374df7 100644 --- a/main/src/main/java/st/slex/messenger/main/ui/single_chat/SingleChatFragment.kt +++ b/main/src/main/java/st/slex/messenger/main/ui/single_chat/SingleChatFragment.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -88,11 +89,11 @@ class SingleChatFragment : BaseFragment() { private val sendClicker: View.OnClickListener get() = View.OnClickListener { - val message = binding.singleChatRecyclerTextInput.editText?.text.toString() + val message = binding.editText.text.toString() if (message.isEmpty()) { val snackBar = Snackbar.make(binding.root, "Empty message", Snackbar.LENGTH_SHORT) - snackBar.anchorView = binding.singleChatRecyclerTextInput + snackBar.anchorView = binding.editText snackBar.setAction("Ok") {} snackBar.show() } else { @@ -107,7 +108,14 @@ class SingleChatFragment : BaseFragment() { private suspend fun collect(result: Resource) = withContext(Dispatchers.Main) { if (result is Resource.Success) { binding.singleChatRecycler.scrollToPosition(adapter.itemCount) - binding.singleChatRecyclerTextInput.editText?.setText("") + val hidingView = requireActivity().currentFocus + val inputMethodManager = + requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.hideSoftInputFromWindow( + hidingView!!.windowToken, + InputMethodManager.RESULT_UNCHANGED_SHOWN + ) + binding.editText.setText("") } } diff --git a/main/src/main/java/st/slex/messenger/main/utilites/base/GlideBase.kt b/main/src/main/java/st/slex/messenger/main/utilites/base/GlideBase.kt index 311db4f..565c36a 100644 --- a/main/src/main/java/st/slex/messenger/main/utilites/base/GlideBase.kt +++ b/main/src/main/java/st/slex/messenger/main/utilites/base/GlideBase.kt @@ -23,11 +23,12 @@ value class GlideBase( needCircleCrop: Boolean = false, needOriginal: Boolean = false ) { - val urlSet = if (url == "null" || url == "") { + val urlSet = if (url == "null" || url.isEmpty()) { R.drawable.ic_default_photo } else url val glide = Glide.with(imageView) .load(urlSet) + .placeholder(R.drawable.ic_default_photo) .listener(primaryRequestListener) if (needCrop) glide.centerCrop() if (needCircleCrop) glide.circleCrop() diff --git a/main/src/main/java/st/slex/messenger/main/utilites/base/SetImageWithGlide.kt b/main/src/main/java/st/slex/messenger/main/utilites/base/SetImageWithGlide.kt index a873590..33d8fa5 100644 --- a/main/src/main/java/st/slex/messenger/main/utilites/base/SetImageWithGlide.kt +++ b/main/src/main/java/st/slex/messenger/main/utilites/base/SetImageWithGlide.kt @@ -19,5 +19,4 @@ class SetImageWithGlide( needCircleCrop: Boolean = false, needOriginal: Boolean = false ) = makeGlideImage(imageView, url, needCrop, needCircleCrop, needOriginal) - } \ No newline at end of file diff --git a/main/src/main/res/layout/fragment_chats.xml b/main/src/main/res/layout/fragment_chats.xml index e60edbf..e21a92f 100644 --- a/main/src/main/res/layout/fragment_chats.xml +++ b/main/src/main/res/layout/fragment_chats.xml @@ -3,22 +3,31 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_screen_drawer_layout" + style="@style/Widget.Material3.DrawerLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="st.slex.messenger.main.ui.chats.ChatsFragment"> - + + @@ -34,24 +43,18 @@ - - - - + - \ No newline at end of file diff --git a/main/src/main/res/layout/fragment_contact.xml b/main/src/main/res/layout/fragment_contact.xml index b8dc016..9153e49 100644 --- a/main/src/main/res/layout/fragment_contact.xml +++ b/main/src/main/res/layout/fragment_contact.xml @@ -12,13 +12,13 @@ android:layout_height="match_parent"> diff --git a/main/src/main/res/layout/fragment_settings.xml b/main/src/main/res/layout/fragment_settings.xml index 757efe1..f45795c 100644 --- a/main/src/main/res/layout/fragment_settings.xml +++ b/main/src/main/res/layout/fragment_settings.xml @@ -1,34 +1,37 @@ + android:layout_height="wrap_content" + app:title="Settings" /> -