From 727a52cc55451642562c7b003b0a0a8f6b0a4d8c Mon Sep 17 00:00:00 2001 From: AndreyYurko Date: Wed, 8 Dec 2021 02:58:58 +0300 Subject: [PATCH 1/4] fix bugs --- .idea/misc.xml | 6 ++ app/build.gradle | 32 +++---- .../firstapp/data/di/module/NetworkModule.kt | 2 +- .../data/di/module/PersistencyModule.kt | 5 +- .../firstapp/data/network/MockApi.kt | 29 +++++-- .../persistent/ProfileInformationStorage.kt | 5 ++ .../firstapp/entity/ProfileInformation.kt | 14 ++-- .../firstapp/interactor/Authinteractor.kt | 4 +- .../andreyyurko/firstapp/ui/MainActivity.kt | 3 +- .../ui/bookmarks/BookmarksFragment.kt | 10 +++ .../EmailConfirmationFragment.kt | 84 ++++++++----------- .../EmailConfirmationViewModel.kt | 84 +++++-------------- .../ui/emailconfirmation/SlotsAdapter.kt | 28 ------- .../VerificationCodeEditText.kt | 24 ++++-- .../firstapp/ui/likes/LikesFragment.kt | 10 +++ .../firstapp/ui/news/NewsFragment.kt | 10 +++ .../ui/onboarding/OnboardingFragment.kt | 37 ++++---- .../firstapp/ui/profile/ProfileFragment.kt | 19 +++++ .../firstapp/ui/profile/ProfileViewModel.kt | 18 +++- .../firstapp/ui/signin/SignInFragment.kt | 9 ++ .../firstapp/ui/signup/SignUpFragment.kt | 46 ++++++---- .../firstapp/ui/signup/SignUpViewModel.kt | 39 +++++---- .../firstapp/ui/userlist/UserListFragment.kt | 6 ++ .../firstapp/ui/userlist/UserListViewModel.kt | 3 +- .../main/res/layout/fragment_bookmarks.xml | 1 + .../layout/fragment_email_confirmation.xml | 2 +- app/src/main/res/layout/fragment_likes.xml | 1 + app/src/main/res/layout/fragment_news.xml | 1 + .../main/res/layout/fragment_onboarding.xml | 2 +- app/src/main/res/layout/fragment_profile.xml | 15 ++-- .../view_verification_code_edit_text.xml | 9 +- ...erification_code_linear_layout_element.xml | 18 ++++ app/src/main/res/values/strings.xml | 1 + 33 files changed, 326 insertions(+), 251 deletions(-) create mode 100644 app/src/main/java/com/andreyyurko/firstapp/data/persistent/ProfileInformationStorage.kt delete mode 100644 app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/SlotsAdapter.kt create mode 100644 app/src/main/res/layout/view_verification_code_linear_layout_element.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 0c23fab..01da05f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -19,15 +19,21 @@ + + + + + + diff --git a/app/build.gradle b/app/build.gradle index 9d548ce..d8ac6bf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,7 +68,7 @@ android { applicationVariants.all { variant -> variant.outputs.all { output -> - outputFileName = "angeni-${variant.versionName}-${variant.buildType.name}.apk" + outputFileName = "yurko-${variant.versionName}-${variant.buildType.name}.apk" } } } @@ -83,65 +83,65 @@ hilt { dependencies { - implementation 'androidx.core:core-ktx:1.6.0' - implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.0' implementation 'com.google.android.material:material:1.4.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.2' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' // HTTP client. def retrofitVersion = "2.9.0" - def okhttpVersion = '4.9.0' + def okhttpVersion = '5.0.0-alpha.3' implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion" implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" implementation "com.squareup.okhttp3:okhttp-tls:$okhttpVersion" - implementation "com.squareup.okhttp3:logging-interceptor:$okhttpVersion" + implementation "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.3" // JSON parser. def moshiVersion = '1.12.0' implementation "com.squareup.moshi:moshi:$moshiVersion" implementation "com.squareup.moshi:moshi-adapters:$moshiVersion" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" // AndroidX. - def lifecycleVersion = "2.4.0-alpha03" + def lifecycleVersion = "2.4.0" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion" - implementation "androidx.activity:activity-ktx:1.4.0-alpha01" + implementation 'androidx.activity:activity-ktx:1.4.0' // Image loading. - def glideVersion = "4.11.0" + def glideVersion = '4.12.0' implementation "com.github.bumptech.glide:glide:$glideVersion" kapt "com.github.bumptech.glide:compiler:$glideVersion" // View binding helper. - implementation "com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.4.4" + implementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.3' // Video player - def exoPlayerVersion = "2.15.0" + def exoPlayerVersion = "2.16.1" implementation "com.google.android.exoplayer:exoplayer-core:$exoPlayerVersion" implementation "com.google.android.exoplayer:exoplayer-ui:$exoPlayerVersion" // RecyclerView adapter delegates. // https://github.com/sockeqwe/AdapterDelegates - def adapterDelegatesVersion = '4.3.0' + def adapterDelegatesVersion = '4.3.1' implementation "com.hannesdorfmann:adapterdelegates4-kotlin-dsl:$adapterDelegatesVersion" implementation "com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:$adapterDelegatesVersion" // Timber logger implementation 'com.jakewharton.timber:timber:5.0.1' // Navigation. - def navigationVersion = "2.4.0-alpha09" + def navigationVersion = '2.4.0-beta02' implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" // System insets handling. // https://chrisbanes.github.io/insetter // https://github.com/chrisbanes/insetter - implementation "dev.chrisbanes.insetter:insetter:0.6.0" + implementation 'dev.chrisbanes.insetter:insetter:0.6.1' // https://github.com/haroldadmin/NetworkResponseAdapter implementation "com.github.haroldadmin:NetworkResponseAdapter:4.2.2" // Dependency injection - def hiltVersion = "2.38.1" + def hiltVersion = '2.40.5' implementation "com.google.dagger:hilt-android:$hiltVersion" kapt "com.google.dagger:hilt-compiler:$hiltVersion" implementation "androidx.hilt:hilt-navigation-fragment:1.0.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" - implementation 'com.google.code.gson:gson:2.8.6' + implementation 'com.google.code.gson:gson:2.8.9' } \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/data/di/module/NetworkModule.kt b/app/src/main/java/com/andreyyurko/firstapp/data/di/module/NetworkModule.kt index c24f099..063dc9a 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/data/di/module/NetworkModule.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/data/di/module/NetworkModule.kt @@ -1,8 +1,8 @@ package com.andreyyurko.firstapp.data.di.module import android.os.Build -import com.andreyyurko.firstapp.data.network.Api import com.andreyyurko.firstapp.BuildConfig +import com.andreyyurko.firstapp.data.network.Api import com.andreyyurko.firstapp.data.network.MockApi import com.andreyyurko.firstapp.data.network.interceptor.AuthorizationInterceptor import com.andreyyurko.firstapp.data.network.interceptor.FirstAppAuthenticator diff --git a/app/src/main/java/com/andreyyurko/firstapp/data/di/module/PersistencyModule.kt b/app/src/main/java/com/andreyyurko/firstapp/data/di/module/PersistencyModule.kt index da8279d..1c1b8e2 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/data/di/module/PersistencyModule.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/data/di/module/PersistencyModule.kt @@ -18,7 +18,10 @@ object PersistencyModule { @Provides @Singleton fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences = - context.getSharedPreferences("our_awesome_app_local_key_value_storage", Context.MODE_PRIVATE) + context.getSharedPreferences( + "our_awesome_app_local_key_value_storage", + Context.MODE_PRIVATE + ) @Provides @Singleton diff --git a/app/src/main/java/com/andreyyurko/firstapp/data/network/MockApi.kt b/app/src/main/java/com/andreyyurko/firstapp/data/network/MockApi.kt index af54fca..8bdfae0 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/data/network/MockApi.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/data/network/MockApi.kt @@ -10,6 +10,8 @@ import com.andreyyurko.firstapp.data.network.response.VerificationTokenResponse import com.andreyyurko.firstapp.entity.User class MockApi : Api { + + private var isAuthorized : Boolean = false override suspend fun getUsers(): NetworkResponse, GetUsersErrorResponce> { return NetworkResponse.Success( body = listOf( @@ -62,14 +64,23 @@ class MockApi : Api { } override suspend fun createProfile(request: CreateProfileRequest): NetworkResponse { - return NetworkResponse.Success( - AuthTokens( - accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2MzgsImV4cCI6MTY0MDg3MTc3MX0.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI", - refreshToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2MzgsImV4cCI6MTY0MDg3MTc3MX0.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI", - accessTokenExpiration = 1640871771000, - refreshTokenExpiration = 1640871771000, - ), - code = 200 - ) + if (isAuthorized) { + return NetworkResponse.Success( + AuthTokens( + accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2MzgsImV4cCI6MTY0MDg3MTc3MX0.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI", + refreshToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2MzgsImV4cCI6MTY0MDg3MTc3MX0.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI", + accessTokenExpiration = 1640871771000, + refreshTokenExpiration = 1640871771000, + ), + code = 200 + ) + } + else { + isAuthorized = true + return NetworkResponse.ServerError( + body = null, + code = 400 + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/data/persistent/ProfileInformationStorage.kt b/app/src/main/java/com/andreyyurko/firstapp/data/persistent/ProfileInformationStorage.kt new file mode 100644 index 0000000..73a5f98 --- /dev/null +++ b/app/src/main/java/com/andreyyurko/firstapp/data/persistent/ProfileInformationStorage.kt @@ -0,0 +1,5 @@ +package com.andreyyurko.firstapp.data.persistent + +class ProfileInformationStorage { + // TODO: добавить SharedPreferences +} \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/entity/ProfileInformation.kt b/app/src/main/java/com/andreyyurko/firstapp/entity/ProfileInformation.kt index 8f785fc..b328e7f 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/entity/ProfileInformation.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/entity/ProfileInformation.kt @@ -1,9 +1,13 @@ package com.andreyyurko.firstapp.entity +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) data class ProfileInformation( - val firstName : String, - val lastName : String, - val nickName : String, - val email : String, - val password : String + @Json(name = "firstname") val firstName : String, + @Json(name = "lastname") val lastName : String, + @Json(name = "nickName") val nickName : String, + @Json(name = "email") val email : String, + @Json(name = "password") val password : String ) \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/interactor/Authinteractor.kt b/app/src/main/java/com/andreyyurko/firstapp/interactor/Authinteractor.kt index d6a4106..7facd6a 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/interactor/Authinteractor.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/interactor/Authinteractor.kt @@ -40,7 +40,7 @@ class AuthInteractor @Inject constructor( verificationToken: String, firstName: String, lastName: String, - userName: String, + nickName: String, password: String ): NetworkResponse { val response = authRepository.generateAuthTokensByEmailAndPersonalInfo( @@ -48,7 +48,7 @@ class AuthInteractor @Inject constructor( verificationToken, firstName, lastName, - userName, + nickName, password ) when (response) { diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/MainActivity.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/MainActivity.kt index 83f31c1..14b3992 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/MainActivity.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/MainActivity.kt @@ -26,13 +26,12 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) subscribeToAuthorizationStatus() + } private fun subscribeToAuthorizationStatus() { lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - // TODO: удалить следующую строчку - viewModel.logout() viewModel.isAuthorized() viewModel.isAuthorizedFlow.collect { showSuitableNavigationFlow(it) diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/bookmarks/BookmarksFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/bookmarks/BookmarksFragment.kt index abf2f81..2ecfce2 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/bookmarks/BookmarksFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/bookmarks/BookmarksFragment.kt @@ -1,13 +1,23 @@ package com.andreyyurko.firstapp.ui.bookmarks +import android.os.Bundle +import android.view.View import androidx.fragment.app.viewModels import by.kirich1409.viewbindingdelegate.viewBinding import com.andreyyurko.firstapp.R import com.andreyyurko.firstapp.databinding.FragmentBookmarksBinding import com.andreyyurko.firstapp.ui.base.BaseFragment +import dev.chrisbanes.insetter.applyInsetter class BookmarksFragment : BaseFragment(R.layout.fragment_bookmarks) { private val viewBinding by viewBinding(FragmentBookmarksBinding::bind) private val viewModel: BookmarksViewModel by viewModels() + + override fun onViewCreated(view : View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewBinding.titleTextView.applyInsetter { + type(statusBars = true) { margin() } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/EmailConfirmationFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/EmailConfirmationFragment.kt index 9f5e8e2..60da237 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/EmailConfirmationFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/EmailConfirmationFragment.kt @@ -12,12 +12,11 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import by.kirich1409.viewbindingdelegate.viewBinding -import com.andreyyurko.firstapp.ui.base.BaseFragment import com.andreyyurko.firstapp.R import com.andreyyurko.firstapp.databinding.FragmentEmailConfirmationBinding -import com.andreyyurko.firstapp.entity.ProfileInformation -import com.google.gson.Gson +import com.andreyyurko.firstapp.ui.base.BaseFragment import dagger.hilt.android.AndroidEntryPoint +import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -31,59 +30,46 @@ class EmailConfirmationFragment : BaseFragment(R.layout.fragment_email_confirmat private val millisRunning : Long = 10000 private val countDownInterval : Long= 1000 private val timer = timer() - private lateinit var profileInformation : ProfileInformation + private var email : String? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - setFragmentResultListener("SignUpProfileInformation") { _, bundle -> - val response = bundle.get("SignUpProfileInformationBundleKey") - profileInformation = Gson().fromJson(response.toString(), ProfileInformation::class.java) + viewBinding.backButton.applyInsetter { + type(statusBars = true) { margin() } } - viewBinding.sendAgainClickableText.isEnabled = false - timer.start() + setFragmentResultListener("SignUpEmail") { _, bundle -> + email = bundle.get("SignUpEmailBundleKey").toString() + email?.let { sendCode()} + } viewBinding.backButton.setOnClickListener { findNavController().popBackStack() } viewBinding.sendAgainClickableText.setOnClickListener { - //Toast.makeText(requireContext(), "Send code please!", Toast.LENGTH_SHORT).show() - viewModel.sendCodeAgain(profileInformation.email) - //subscribeToSendCodeRespond() - - viewBinding.sendAgainClickableText.isEnabled = false - timer().start() + email?.let { sendCode()} } viewBinding.submitButton.setOnClickListener { val code = viewBinding.verificationCodePanel.getCode() - //Toast.makeText(requireContext(), "Submit code: $code!", Toast.LENGTH_SHORT).show() - viewModel.submitCode( - firstName = profileInformation.firstName, - lastName = profileInformation.lastName, - userName = profileInformation.nickName, - email = profileInformation.email, - password = profileInformation.password, - code = code - ) - subscribeToVerifyCodeRespond() - } - } - - private fun subscribeToSendCodeRespond() { - viewLifecycleOwner.lifecycleScope.launch { - viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.sendingActionStateFlow().collect { response -> - when (response) { - is EmailConfirmationViewModel.SendingCodeActionState.SendingSuccess-> { - // Do nothing (ошибка должна вылететь если не получилось код отправить) - // Можно её будет обработать потом - } - else -> { - // Do nothing. + email?.let { it -> viewModel.submitCode(it, code) } + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.confirmationActionStateFlow().collect { response -> + when (response) { + is EmailConfirmationViewModel.ConfirmationCodeActionState.SubmissionCodeSuccess-> { + viewModel.refreshSuccess() + val controller = findNavController() + if (controller.currentDestination == controller.findDestination(R.id.emailConfirmationFragment)){ + controller.popBackStack() + } + Toast.makeText(requireContext(), "Подтверждение кода прошло успешно!", Toast.LENGTH_SHORT).show() + } + else -> { + //TODO: обработать ошибки + } } } } @@ -91,17 +77,19 @@ class EmailConfirmationFragment : BaseFragment(R.layout.fragment_email_confirmat } } - private fun subscribeToVerifyCodeRespond() { - // Можно тут в последствии добавить иконку загрузки + private fun sendCode() { + email?.let { viewModel.sendCode(it) } + viewBinding.sendAgainClickableText.isEnabled = false + timer.start() viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.confirmationActionStateFlow().collect { response -> + viewModel.sendingActionStateFlow().collect { response -> when (response) { - is EmailConfirmationViewModel.SubmissionCodeActionState.SubmissionCodeSuccess-> { - Toast.makeText(requireContext(), "Done!", Toast.LENGTH_SHORT).show() + is EmailConfirmationViewModel.SendingCodeActionState.SendingSuccess-> { + // Do nothing } else -> { - // Do nothing. + // TODO: обработать случаи ошибки } } } @@ -110,13 +98,13 @@ class EmailConfirmationFragment : BaseFragment(R.layout.fragment_email_confirmat } override fun onPause() { - super.onPause() timer.cancel() + super.onPause() } override fun onDestroy() { - super.onDestroy() timer.cancel() + super.onDestroy() } private fun timer() : CountDownTimer { diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/EmailConfirmationViewModel.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/EmailConfirmationViewModel.kt index ca37046..ea80a36 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/EmailConfirmationViewModel.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/EmailConfirmationViewModel.kt @@ -19,11 +19,11 @@ import javax.inject.Inject class EmailConfirmationViewModel @Inject constructor( private val authInteractor: AuthInteractor ) : BaseViewModel() { - private val _emailConfirmationActionStateFlow = MutableStateFlow(SubmissionCodeActionState.Loading) + private val _emailConfirmationActionStateFlow = MutableStateFlow(ConfirmationCodeActionState.Loading) private val _codeSendingActionStateFlow = MutableStateFlow(SendingCodeActionState.Loading) - fun confirmationActionStateFlow(): Flow { + fun confirmationActionStateFlow(): Flow { return _emailConfirmationActionStateFlow.asStateFlow() } @@ -33,81 +33,35 @@ class EmailConfirmationViewModel @Inject constructor( fun submitCode( email: String, - firstName: String, - lastName: String, - userName: String, - password: String, code: String ) { viewModelScope.launch { - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.Loading) + _emailConfirmationActionStateFlow.emit(ConfirmationCodeActionState.Loading) try { when (val response = authInteractor.verifyRegistrationCode(email, code)) { is NetworkResponse.Success -> { - val verificationToken = response.body.verificationToken - createProfile( - email = email, - verificationToken = verificationToken, - firstName = firstName, - lastName = lastName, - userName = userName, - password = password - ) + _emailConfirmationActionStateFlow.emit(ConfirmationCodeActionState.SubmissionCodeSuccess) } is NetworkResponse.ServerError -> { - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.VerificationServerError(response)) + _emailConfirmationActionStateFlow.emit(ConfirmationCodeActionState.VerificationServerError(response)) } is NetworkResponse.NetworkError -> { - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.NetworkError(response)) + _emailConfirmationActionStateFlow.emit(ConfirmationCodeActionState.NetworkError(response)) } is NetworkResponse.UnknownError -> { - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.UnknownError(response)) + _emailConfirmationActionStateFlow.emit(ConfirmationCodeActionState.UnknownError(response)) } } } catch (error: Throwable) { Timber.e(error) - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.UnknownError(NetworkResponse.UnknownError(error))) + _emailConfirmationActionStateFlow.emit(ConfirmationCodeActionState.UnknownError(NetworkResponse.UnknownError(error))) } } } - private suspend fun createProfile( - email: String, - verificationToken: String, - firstName: String, - lastName: String, - userName: String, - password: String - ) { - try { - when (val response = authInteractor.createProfile( - email = email, - verificationToken = verificationToken, - firstName = firstName, - lastName = lastName, - userName = userName, - password = password - )) { - is NetworkResponse.Success -> { - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.SubmissionCodeSuccess) - } - is NetworkResponse.ServerError -> { - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.CreateProfileServerError(response)) - } - is NetworkResponse.NetworkError -> { - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.NetworkError(response)) - } - is NetworkResponse.UnknownError -> { - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.UnknownError(response)) - } - } - } catch (error: Throwable) { - Timber.e(error) - _emailConfirmationActionStateFlow.emit(SubmissionCodeActionState.UnknownError(NetworkResponse.UnknownError(error))) - } - } - fun sendCodeAgain(email: String) { + + fun sendCode(email: String) { viewModelScope.launch { _codeSendingActionStateFlow.emit(SendingCodeActionState.Loading) try { @@ -132,13 +86,16 @@ class EmailConfirmationViewModel @Inject constructor( } } - sealed class SubmissionCodeActionState { - object SubmissionCodeSuccess : SubmissionCodeActionState() - object Loading : SubmissionCodeActionState() - data class VerificationServerError(val e: NetworkResponse.ServerError) : SubmissionCodeActionState() - data class CreateProfileServerError(val e: NetworkResponse.ServerError) : SubmissionCodeActionState() - data class NetworkError(val e: NetworkResponse.NetworkError) : SubmissionCodeActionState() - data class UnknownError(val e: NetworkResponse.UnknownError) : SubmissionCodeActionState() + suspend fun refreshSuccess() { + _emailConfirmationActionStateFlow.emit(ConfirmationCodeActionState.Loading) + } + + sealed class ConfirmationCodeActionState { + object SubmissionCodeSuccess : ConfirmationCodeActionState() + object Loading : ConfirmationCodeActionState() + data class VerificationServerError(val e: NetworkResponse.ServerError) : ConfirmationCodeActionState() + data class NetworkError(val e: NetworkResponse.NetworkError) : ConfirmationCodeActionState() + data class UnknownError(val e: NetworkResponse.UnknownError) : ConfirmationCodeActionState() } sealed class SendingCodeActionState { @@ -148,4 +105,5 @@ class EmailConfirmationViewModel @Inject constructor( data class NetworkError(val e: NetworkResponse.NetworkError) : SendingCodeActionState() data class UnknownError(val e: NetworkResponse.UnknownError) : SendingCodeActionState() } + } \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/SlotsAdapter.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/SlotsAdapter.kt deleted file mode 100644 index e9ac258..0000000 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/SlotsAdapter.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.andreyyurko.firstapp.ui.emailconfirmation - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.andreyyurko.firstapp.R -import com.andreyyurko.firstapp.entity.User -import com.bumptech.glide.Glide - -abstract class SlotsAdapter : RecyclerView.Adapter() { - var slotsList: List = emptyList() - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val itemView = LayoutInflater.from(parent.context).inflate(R.layout.view_verification_code_slot, parent, false) - return ViewHolder(itemView) - } - - override fun getItemCount(): Int { - return slotsList.size - } - - class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - val userNameTextView = itemView.findViewById(R.id.userNameTextView) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/VerificationCodeEditText.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/VerificationCodeEditText.kt index 17ab4b9..6f43772 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/VerificationCodeEditText.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/emailconfirmation/VerificationCodeEditText.kt @@ -12,7 +12,17 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import com.andreyyurko.firstapp.R import com.andreyyurko.firstapp.databinding.ViewVerificationCodeEditTextBinding +import com.andreyyurko.firstapp.databinding.ViewVerificationCodeSlotBinding import kotlin.properties.Delegates +import android.view.View +import android.widget.LinearLayout + + + + + + + class VerificationCodeEditText @JvmOverloads constructor( @@ -26,11 +36,13 @@ private val viewBinding = ViewVerificationCodeEditTextBinding.inflate(LayoutInflater.from(context), this) var numberOfSlots: Int by Delegates.observable(0) { _, _, newValue -> - println(newValue) - val linearLayout = viewBinding.slotContainer for (i in 0 until newValue) { - val view = VerificationCodeSlotView(context) + LayoutInflater.from(context).inflate( + R.layout.view_verification_code_linear_layout_element, linearLayout + ) + // TODO: настраивать размер в зависимости от числа ячеек + /*val view = VerificationCodeSlotView(context) val width = resources.getDimension(R.dimen.verification_code_slot_width).toInt() val height = resources.getDimension(R.dimen.verification_code_slot_height).toInt() val margStart = resources.getDimension(R.dimen.edit_text_half_horizontal_padding).toInt() @@ -41,18 +53,14 @@ var numberOfSlots: Int by Delegates.observable(0) { _, _, newValue -> marginStart = margStart marginEnd = margEnd } - linearLayout.addView(view, i) + linearLayout.addView(view)*/ } - println("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") - println(linearLayout.childCount) - slotViews = mutableListOf().apply { for (i in 0 until newValue) { this.add(linearLayout.getChildAt(i) as VerificationCodeSlotView) } } - println(linearLayout.childCount) slotValues = Array(newValue) { null } diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/likes/LikesFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/likes/LikesFragment.kt index 3607f0c..181c062 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/likes/LikesFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/likes/LikesFragment.kt @@ -1,13 +1,23 @@ package com.andreyyurko.firstapp.ui.likes +import android.os.Bundle +import android.view.View import androidx.fragment.app.viewModels import by.kirich1409.viewbindingdelegate.viewBinding import com.andreyyurko.firstapp.R import com.andreyyurko.firstapp.databinding.FragmentLikesBinding import com.andreyyurko.firstapp.ui.base.BaseFragment +import dev.chrisbanes.insetter.applyInsetter class LikesFragment : BaseFragment(R.layout.fragment_likes) { private val viewBinding by viewBinding(FragmentLikesBinding::bind) private val viewModel: LikesViewModel by viewModels() + + override fun onViewCreated(view : View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewBinding.titleTextView.applyInsetter { + type(statusBars = true) { margin() } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/news/NewsFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/news/NewsFragment.kt index cc90af7..bd32d28 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/news/NewsFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/news/NewsFragment.kt @@ -1,14 +1,24 @@ package com.andreyyurko.firstapp.ui.news +import android.os.Bundle +import android.view.View import androidx.fragment.app.viewModels import by.kirich1409.viewbindingdelegate.viewBinding import com.andreyyurko.firstapp.R import com.andreyyurko.firstapp.databinding.FragmentNewsBinding import com.andreyyurko.firstapp.ui.base.BaseFragment +import dev.chrisbanes.insetter.applyInsetter class NewsFragment : BaseFragment(R.layout.fragment_news) { private val viewBinding by viewBinding(FragmentNewsBinding::bind) private val viewModel: NewsViewModel by viewModels() + + override fun onViewCreated(view : View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewBinding.titleTextView.applyInsetter { + type(statusBars = true) { margin() } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/onboarding/OnboardingFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/onboarding/OnboardingFragment.kt index 476bd76..7589a38 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/onboarding/OnboardingFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/onboarding/OnboardingFragment.kt @@ -33,6 +33,16 @@ class OnboardingFragment : BaseFragment(R.layout.fragment_onboarding) { private var millisRunning: Long = 2000 private var countDownInterval: Long = 100 + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + player = SimpleExoPlayer.Builder(requireContext()).build().apply { + addMediaItem(MediaItem.fromUri("asset:///onboarding.mp4")) + repeatMode = Player.REPEAT_MODE_ALL + prepare() + } + + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewBinding.volumeControlButton.applyInsetter { @@ -41,14 +51,7 @@ class OnboardingFragment : BaseFragment(R.layout.fragment_onboarding) { viewBinding.signUpButton.applyInsetter { type(navigationBars = true) { margin() } } - - player = SimpleExoPlayer.Builder(requireContext()).build().apply { - addMediaItem(MediaItem.fromUri("asset:///onboarding.mp4")) - repeatMode = Player.REPEAT_MODE_ALL - prepare() - } viewBinding.playerView.player = player - viewBinding.volumeControlButton.setOnClickListener { changeVolume() } @@ -62,7 +65,7 @@ class OnboardingFragment : BaseFragment(R.layout.fragment_onboarding) { val pageTransformer = ViewPager2.PageTransformer { page: View, position: Float -> page.translationX = -pageTranslationX * position // Next line scales the item's height. You can remove it if you don't want this effect - page.scaleY = 1 - (0.30f * abs(position)) + //page.scaleY = 1 - (0.30f * abs(position)) // If you want a fading effect uncomment the next line: page.alpha = 0.25f + (1 - abs(position)) } @@ -123,12 +126,12 @@ class OnboardingFragment : BaseFragment(R.layout.fragment_onboarding) { if (isVolumeOn) { player?.volume = 0F isVolumeOn = false - viewBinding.volumeControlButton.setImageResource(R.drawable.ic_volume_up_white_24dp) + viewBinding.volumeControlButton.setImageResource(R.drawable.ic_volume_off_white_24dp) } else { player?.volume = 1F isVolumeOn = true - viewBinding.volumeControlButton.setImageResource(R.drawable.ic_volume_off_white_24dp) + viewBinding.volumeControlButton.setImageResource(R.drawable.ic_volume_up_white_24dp) } } @@ -146,13 +149,15 @@ class OnboardingFragment : BaseFragment(R.layout.fragment_onboarding) { } override fun onFinish() { - if (viewBinding.viewPager.adapter?.itemCount == page + 1) { - page = 0 - } else { - page++ + if (!timerIsStop) { + if (viewBinding.viewPager.adapter?.itemCount == page + 1) { + page = 0 + } else { + page++ + } + viewBinding.viewPager.setCurrentItem(page, true) + timer().start() } - viewBinding.viewPager.setCurrentItem(page, true) - timer().start() } } } diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileFragment.kt index e76fc08..85a017d 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileFragment.kt @@ -1,13 +1,32 @@ package com.andreyyurko.firstapp.ui.profile +import android.os.Bundle +import android.view.View import androidx.fragment.app.viewModels import by.kirich1409.viewbindingdelegate.viewBinding import com.andreyyurko.firstapp.R import com.andreyyurko.firstapp.databinding.FragmentProfileBinding import com.andreyyurko.firstapp.ui.base.BaseFragment +import dagger.hilt.android.AndroidEntryPoint +import dev.chrisbanes.insetter.applyInsetter +@AndroidEntryPoint class ProfileFragment : BaseFragment(R.layout.fragment_profile) { private val viewBinding by viewBinding(FragmentProfileBinding::bind) private val viewModel: ProfileViewModel by viewModels() + + override fun onViewCreated(view : View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewBinding.logOutButton.applyInsetter { + type(statusBars = true) { margin() } + } + + viewBinding.logOutButton.setOnClickListener { + viewModel.logout() + } + + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileViewModel.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileViewModel.kt index 23efd99..bcb82fe 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileViewModel.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileViewModel.kt @@ -1,7 +1,19 @@ package com.andreyyurko.firstapp.ui.profile +import androidx.lifecycle.viewModelScope +import com.andreyyurko.firstapp.interactor.AuthInteractor import com.andreyyurko.firstapp.ui.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject -class ProfileViewModel : BaseViewModel(){ - -} +@HiltViewModel +class ProfileViewModel @Inject constructor( + private val authInteractor: AuthInteractor +) : BaseViewModel() { + fun logout() { + viewModelScope.launch { + authInteractor.logout() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt index 5f54b9f..4f1b5c1 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt @@ -12,6 +12,7 @@ import com.andreyyurko.firstapp.R import com.andreyyurko.firstapp.databinding.FragmentSignInBinding import com.andreyyurko.firstapp.ui.base.BaseFragment import dagger.hilt.android.AndroidEntryPoint +import dev.chrisbanes.insetter.applyInsetter @AndroidEntryPoint class SignInFragment : BaseFragment(R.layout.fragment_sign_in) { @@ -21,6 +22,7 @@ class SignInFragment : BaseFragment(R.layout.fragment_sign_in) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + requireActivity().onBackPressedDispatcher.addCallback( this, object : OnBackPressedCallback(true) { @@ -33,6 +35,13 @@ class SignInFragment : BaseFragment(R.layout.fragment_sign_in) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + viewBinding.backButton.applyInsetter { + type(statusBars = true) { margin() } + } + viewBinding.signInButton.applyInsetter { + type(navigationBars = true) { margin() } + } viewBinding.backButton.setOnClickListener { onBackButtonPressed() } diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpFragment.kt index cf293ae..0e8fbab 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpFragment.kt @@ -24,14 +24,14 @@ import androidx.navigation.fragment.findNavController import by.kirich1409.viewbindingdelegate.viewBinding import com.andreyyurko.firstapp.R import com.andreyyurko.firstapp.databinding.FragmentSignUpBinding -import com.andreyyurko.firstapp.entity.ProfileInformation import com.andreyyurko.firstapp.ui.base.BaseFragment import com.andreyyurko.firstapp.util.getSpannedString -import com.google.gson.Gson import dagger.hilt.android.AndroidEntryPoint +import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.launch import kotlinx.coroutines.flow.collect + @AndroidEntryPoint class SignUpFragment : BaseFragment(R.layout.fragment_sign_up) { @@ -39,8 +39,14 @@ class SignUpFragment : BaseFragment(R.layout.fragment_sign_up) { private val viewBinding by viewBinding(FragmentSignUpBinding::bind) + private val verificationCode : String = "" + + //private val moshi: Moshi = Moshi.Builder().build() + //private val jsonAdapter: JsonAdapter = moshi.adapter(ProfileInformation::class.java) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + requireActivity().onBackPressedDispatcher.addCallback( this, object : OnBackPressedCallback(true) { @@ -53,13 +59,26 @@ class SignUpFragment : BaseFragment(R.layout.fragment_sign_up) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + viewBinding.backButton.applyInsetter { + type(statusBars = true) { margin() } + } + viewBinding.signUpButton.applyInsetter { + type(navigationBars = true) { margin() } + } + subscribeToEvents() viewBinding.backButton.setOnClickListener { onBackButtonPressed() } viewBinding.signUpButton.setOnClickListener { - viewModel.sendVerificationCode( - email = viewBinding.emailEditText.text?.toString() ?: "" + viewModel.signUp( + firstName = viewBinding.firstnameEditText.text?.toString() ?: "", + lastName = viewBinding.lastnameEditText.text?.toString() ?: "", + nickName = viewBinding.nicknameEditText.text?.toString() ?: "", + email = viewBinding.emailEditText.text?.toString() ?: "", + password = viewBinding.passwordEditText.text?.toString() ?: "", + verificationToken = verificationCode ) } subscribeToFormFields() @@ -159,18 +178,13 @@ class SignUpFragment : BaseFragment(R.layout.fragment_sign_up) { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.signUpActionStateFlow().collect { event -> when (event) { - is SignUpViewModel.SignUpActionState.SendVerificationCodeSucess-> { - // TODO: можно сделать проверку на непустоту, но кажется кнопка за это отвечает + is SignUpViewModel.SignUpActionState.SignUpSuccess-> { + // Do nothing + } + is SignUpViewModel.SignUpActionState.ServerError-> { setFragmentResult( - "SignUpProfileInformation", bundleOf("SignUpProfileInformationBundleKey" to Gson().toJson( - ProfileInformation( - firstName = viewBinding.firstnameEditText.text.toString(), - lastName = viewBinding.lastnameEditText.text.toString(), - nickName = viewBinding.nicknameEditText.text.toString(), - email = viewBinding.emailEditText.text.toString(), - password = viewBinding.passwordEditText.text.toString() - ) - ) + "SignUpEmail", bundleOf("SignUpEmailBundleKey" to + viewBinding.emailEditText.text.toString() ) ) viewModel.refreshSuccess() @@ -178,8 +192,6 @@ class SignUpFragment : BaseFragment(R.layout.fragment_sign_up) { if (controller.currentDestination == controller.findDestination(R.id.signUpFragment)){ controller.navigate(R.id.emailConfirmationFragment) } - //findNavController().navigate(R.id.emailConfirmationFragment) - } else -> { // Do nothing. diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpViewModel.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpViewModel.kt index 8144b90..4e5dab8 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpViewModel.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpViewModel.kt @@ -1,7 +1,8 @@ package com.andreyyurko.firstapp.ui.signup import androidx.lifecycle.viewModelScope -import com.andreyyurko.firstapp.data.network.response.error.SendRegistrationVerificationCodeErrorResponse +import com.andreyyurko.firstapp.data.network.response.error.CreateProfileErrorResponse +import com.andreyyurko.firstapp.entity.ProfileInformation import com.andreyyurko.firstapp.interactor.AuthInteractor import com.andreyyurko.firstapp.ui.base.BaseViewModel import com.haroldadmin.cnradapter.NetworkResponse @@ -26,28 +27,30 @@ class SignUpViewModel @Inject constructor( } suspend fun refreshSuccess() { - _signUpActionStateFlow.emit(SignUpActionState.SendingDone) + _signUpActionStateFlow.emit(SignUpActionState.NotVerifiedYet) } - fun sendVerificationCode( - email: String + fun signUp( + email: String, + verificationToken: String, + firstName: String, + lastName: String, + nickName: String, + password: String ) { - // если email тот же, что был раньше, то нам не нужно запускать все заново - if (email == _email) { - viewModelScope.launch { - _signUpActionStateFlow.emit(SignUpActionState.SendVerificationCodeSucess) - } - return - } - _email = email viewModelScope.launch { _signUpActionStateFlow.emit(SignUpActionState.Loading) try { - when (val response = authInteractor.sendRegistrationVerificationCode( - email + when (val response = authInteractor.createProfile( + email = email, + verificationToken = verificationToken, + firstName = firstName, + lastName = lastName, + nickName = nickName, + password = password )) { is NetworkResponse.Success -> { - _signUpActionStateFlow.emit(SignUpActionState.SendVerificationCodeSucess) + _signUpActionStateFlow.emit(SignUpActionState.SignUpSuccess) } is NetworkResponse.ServerError -> { _signUpActionStateFlow.emit(SignUpActionState.ServerError(response)) @@ -67,10 +70,10 @@ class SignUpViewModel @Inject constructor( } sealed class SignUpActionState { - object SendVerificationCodeSucess : SignUpActionState() + object SignUpSuccess : SignUpActionState() object Loading : SignUpActionState() - object SendingDone : SignUpActionState() - data class ServerError(val e: NetworkResponse.ServerError) : SignUpActionState() + object NotVerifiedYet : SignUpActionState() + data class ServerError(val e: NetworkResponse.ServerError) : SignUpActionState() data class NetworkError(val e: NetworkResponse.NetworkError) : SignUpActionState() data class UnknownError(val e: NetworkResponse.UnknownError) : SignUpActionState() } diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/userlist/UserListFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/userlist/UserListFragment.kt index 9e76d11..7becb18 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/userlist/UserListFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/userlist/UserListFragment.kt @@ -15,6 +15,7 @@ import com.andreyyurko.firstapp.databinding.FragmentUserListBinding import com.andreyyurko.firstapp.ui.base.BaseFragment import com.google.android.exoplayer2.util.Log import dagger.hilt.android.AndroidEntryPoint +import dev.chrisbanes.insetter.applyInsetter import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -32,6 +33,7 @@ class UserListFragment : BaseFragment(R.layout.fragment_user_list) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProvider(this)[UserListViewModel::class.java] + } //@SuppressLint("UnsafeRepeatOnLifecycleDetector") @@ -39,6 +41,10 @@ class UserListFragment : BaseFragment(R.layout.fragment_user_list) { //val viewModel : MainViewModel = ViewModelProvider(this)[MainViewModel::class.java] Log.d(LOG_TAG, "onCreate()") super.onViewCreated(view, savedInstanceState) + + viewBinding.usersRecyclerView.applyInsetter { + type(statusBars = true) { margin() } + } //setContentView(android.R.layout.fragment_user_list) setupRecyclerView() diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/userlist/UserListViewModel.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/userlist/UserListViewModel.kt index 668a2fb..ae63ba9 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/userlist/UserListViewModel.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/userlist/UserListViewModel.kt @@ -87,6 +87,7 @@ private fun provideOkHttpClient(): OkHttpClient { } private fun provideMoshi(): Moshi { - return Moshi.Builder().build() + return Moshi.Builder() + .build() } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_bookmarks.xml b/app/src/main/res/layout/fragment_bookmarks.xml index 9398699..9306ada 100644 --- a/app/src/main/res/layout/fragment_bookmarks.xml +++ b/app/src/main/res/layout/fragment_bookmarks.xml @@ -4,6 +4,7 @@ android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> + app:vcet_numberOfSlots="5" /> diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml index f9a4f24..6946a76 100644 --- a/app/src/main/res/layout/fragment_profile.xml +++ b/app/src/main/res/layout/fragment_profile.xml @@ -3,13 +3,14 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> - + android:layout_marginHorizontal="24dp" + android:layout_marginTop="16dp" + android:text="@string/profileLogOutbutton" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintEnd_toEndOf="parent"/> \ No newline at end of file diff --git a/app/src/main/res/layout/view_verification_code_edit_text.xml b/app/src/main/res/layout/view_verification_code_edit_text.xml index feaeb0c..173b2ae 100644 --- a/app/src/main/res/layout/view_verification_code_edit_text.xml +++ b/app/src/main/res/layout/view_verification_code_edit_text.xml @@ -8,13 +8,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2ba4264..d352fb2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,4 +34,5 @@ Подтвердить код Отправить код еще раз Осталось: t секунд + Выйти \ No newline at end of file From de7e0a4561303499db9029b80cc21cd49105710a Mon Sep 17 00:00:00 2001 From: AndreyYurko Date: Wed, 8 Dec 2021 04:28:34 +0300 Subject: [PATCH 2/4] animation done --- .../firstapp/ui/signin/SignInFragment.kt | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt index 4f1b5c1..cfe4ecd 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt @@ -1,8 +1,12 @@ package com.andreyyurko.firstapp.ui.signin +import android.animation.AnimatorSet +import android.animation.ObjectAnimator import android.app.AlertDialog import android.os.Bundle import android.view.View +import android.view.animation.AccelerateInterpolator +import android.view.animation.Animation import androidx.activity.OnBackPressedCallback import androidx.core.widget.doAfterTextChanged import androidx.fragment.app.viewModels @@ -35,6 +39,7 @@ class SignInFragment : BaseFragment(R.layout.fragment_sign_in) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + AnimateLogo() viewBinding.backButton.applyInsetter { type(statusBars = true) { margin() } @@ -73,6 +78,44 @@ class SignInFragment : BaseFragment(R.layout.fragment_sign_in) { } } + private fun AnimateLogo(){ + val currentY: Float= viewBinding.mknLogoImageView.translationY + val mknMoveAnimator = ObjectAnimator.ofFloat( + viewBinding.mknLogoImageView, + "translationY", + currentY, + -25f, + currentY + ) + mknMoveAnimator.interpolator = AccelerateInterpolator(0.5f) + mknMoveAnimator.repeatCount = Animation.INFINITE + + + val mknScaleXAnimator = ObjectAnimator.ofFloat( + viewBinding.mknLogoImageView, + "scaleX", + 1F, + 1.1F, + 1F + ) + mknScaleXAnimator.repeatCount = Animation.INFINITE + + val mknScaleYAnimator = ObjectAnimator.ofFloat( + viewBinding.mknLogoImageView, + "scaleY", + 1F, + 1.05F, + 1F + ) + mknScaleYAnimator.repeatCount = Animation.INFINITE + + val mknAnimatorSet = AnimatorSet() + mknAnimatorSet.play(mknMoveAnimator).with(mknScaleXAnimator).with(mknScaleYAnimator) + mknAnimatorSet.duration = 2000 + + mknAnimatorSet.start() + } + private fun onBackButtonPressed() { val email = viewBinding.emailEditText.text?.toString() val password = viewBinding.passwordEditText.text?.toString() From 7211fa87482e78ec8ed291f2ea5cf61203810142 Mon Sep 17 00:00:00 2001 From: AndreyYurko Date: Wed, 8 Dec 2021 04:30:14 +0300 Subject: [PATCH 3/4] animation done --- .../java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt index cfe4ecd..ee08cb7 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInFragment.kt @@ -39,7 +39,7 @@ class SignInFragment : BaseFragment(R.layout.fragment_sign_in) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - AnimateLogo() + animateLogo() viewBinding.backButton.applyInsetter { type(statusBars = true) { margin() } @@ -78,7 +78,7 @@ class SignInFragment : BaseFragment(R.layout.fragment_sign_in) { } } - private fun AnimateLogo(){ + private fun animateLogo(){ val currentY: Float= viewBinding.mknLogoImageView.translationY val mknMoveAnimator = ObjectAnimator.ofFloat( viewBinding.mknLogoImageView, From c7feb747d937034b336bbd6491358fee1dba6fd4 Mon Sep 17 00:00:00 2001 From: AndreyYurko Date: Wed, 8 Dec 2021 15:04:28 +0300 Subject: [PATCH 4/4] add profileInformation --- .../data/di/module/PersistencyModule.kt | 6 ++ .../persistent/ProfileInformationStorage.kt | 50 ++++++++++++++- .../ProfileInformationInteractor.kt | 23 +++++++ .../ProfileInformationRepository.kt | 40 ++++++++++++ .../firstapp/ui/profile/ProfileFragment.kt | 19 ++++++ .../firstapp/ui/profile/ProfileViewModel.kt | 19 +++++- .../firstapp/ui/signin/SignInViewModel.kt | 1 + .../firstapp/ui/signup/SignUpViewModel.kt | 11 +++- app/src/main/res/layout/fragment_profile.xml | 63 +++++++++++++++++-- 9 files changed, 224 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/andreyyurko/firstapp/interactor/ProfileInformationInteractor.kt create mode 100644 app/src/main/java/com/andreyyurko/firstapp/repository/ProfileInformationRepository.kt diff --git a/app/src/main/java/com/andreyyurko/firstapp/data/di/module/PersistencyModule.kt b/app/src/main/java/com/andreyyurko/firstapp/data/di/module/PersistencyModule.kt index 1c1b8e2..1195a41 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/data/di/module/PersistencyModule.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/data/di/module/PersistencyModule.kt @@ -3,6 +3,7 @@ package com.andreyyurko.firstapp.data.di.module import android.content.Context import android.content.SharedPreferences import com.andreyyurko.firstapp.data.persistent.LocalKeyValueStorage +import com.andreyyurko.firstapp.data.persistent.ProfileInformationStorage import com.squareup.moshi.Moshi import dagger.Module import dagger.Provides @@ -27,4 +28,9 @@ object PersistencyModule { @Singleton fun provideLocalKeyValueStorage(pref: SharedPreferences, moshi: Moshi): LocalKeyValueStorage = LocalKeyValueStorage(pref, moshi) + + @Provides + @Singleton + fun provideProfileInformationStorage(pref: SharedPreferences, moshi: Moshi): ProfileInformationStorage = + ProfileInformationStorage(pref, moshi) } \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/data/persistent/ProfileInformationStorage.kt b/app/src/main/java/com/andreyyurko/firstapp/data/persistent/ProfileInformationStorage.kt index 73a5f98..b9d4f90 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/data/persistent/ProfileInformationStorage.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/data/persistent/ProfileInformationStorage.kt @@ -1,5 +1,51 @@ package com.andreyyurko.firstapp.data.persistent -class ProfileInformationStorage { - // TODO: добавить SharedPreferences +import android.content.SharedPreferences +import androidx.core.content.edit +import com.andreyyurko.firstapp.entity.ProfileInformation +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonDataException +import com.squareup.moshi.Moshi +import timber.log.Timber +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +class ProfileInformationStorage( + private val pref: SharedPreferences, + moshi: Moshi +) { + var profileInformation: ProfileInformation? by JsonDelegate( + PROFILE_INFORMATION, + pref, + moshi.adapter(ProfileInformation::class.java) + ) + + + private class JsonDelegate( + private val key: String, + private val pref: SharedPreferences, + private val adapter: JsonAdapter + ) : ReadWriteProperty { + + override fun setValue(thisRef: ProfileInformationStorage, property: KProperty<*>, value: T?) { + pref.edit(commit = true) { + if (value == null) remove(key) + else putString(key, adapter.toJson(value)) + } + } + + override fun getValue(thisRef: ProfileInformationStorage, property: KProperty<*>): T? { + return try { + pref.getString(key, null)?.let { adapter.fromJson(it) } + } catch (e: JsonDataException) { + Timber.e(e) + setValue(thisRef, property, null) + null + } + } + } + + companion object { + private const val PROFILE_INFORMATION = "profile_information" + } } \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/interactor/ProfileInformationInteractor.kt b/app/src/main/java/com/andreyyurko/firstapp/interactor/ProfileInformationInteractor.kt new file mode 100644 index 0000000..87e40dc --- /dev/null +++ b/app/src/main/java/com/andreyyurko/firstapp/interactor/ProfileInformationInteractor.kt @@ -0,0 +1,23 @@ +package com.andreyyurko.firstapp.interactor + +import com.andreyyurko.firstapp.entity.ProfileInformation +import com.andreyyurko.firstapp.repository.ProfileInformationRepository +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class ProfileInformationInteractor @Inject constructor( + private val profileInformationRepository: ProfileInformationRepository +) { + suspend fun saveProfileInformation(profileInformation : ProfileInformation) { + profileInformationRepository.saveProfileInformation(profileInformation) + } + + suspend fun getProfileInformation(): StateFlow { + return profileInformationRepository.getProfileInformationFlow() + } + + suspend fun logout() { + profileInformationRepository.saveProfileInformation(null) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/repository/ProfileInformationRepository.kt b/app/src/main/java/com/andreyyurko/firstapp/repository/ProfileInformationRepository.kt new file mode 100644 index 0000000..12c0ffc --- /dev/null +++ b/app/src/main/java/com/andreyyurko/firstapp/repository/ProfileInformationRepository.kt @@ -0,0 +1,40 @@ +package com.andreyyurko.firstapp.repository + +import com.andreyyurko.firstapp.data.persistent.ProfileInformationStorage +import com.andreyyurko.firstapp.entity.ProfileInformation +import com.blelocking.di.AppCoroutineScope +import com.blelocking.di.IoCoroutineDispatcher +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton + + +@Singleton +class ProfileInformationRepository @Inject constructor( + private val profileInformationStorage: ProfileInformationStorage, + @AppCoroutineScope externalCoroutineScope: CoroutineScope, + @IoCoroutineDispatcher private val ioDispatcher: CoroutineDispatcher +) { + suspend fun saveProfileInformation(profileInformation: ProfileInformation?) { + withContext(ioDispatcher) { + Timber.d("Save profile information $profileInformation") + profileInformationStorage.profileInformation = profileInformation + } + } + + suspend fun getProfileInformationFlow() : StateFlow { + return profileInformationFlow.await().asStateFlow() + } + + private val profileInformationFlow: Deferred> = + externalCoroutineScope.async(context = ioDispatcher, start = CoroutineStart.LAZY) { + Timber.d("Initializing auth tokens flow.") + MutableStateFlow( + profileInformationStorage.profileInformation + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileFragment.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileFragment.kt index 85a017d..2881a6f 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileFragment.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileFragment.kt @@ -3,12 +3,17 @@ package com.andreyyurko.firstapp.ui.profile import android.os.Bundle import android.view.View import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import by.kirich1409.viewbindingdelegate.viewBinding import com.andreyyurko.firstapp.R import com.andreyyurko.firstapp.databinding.FragmentProfileBinding import com.andreyyurko.firstapp.ui.base.BaseFragment import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.applyInsetter +import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.collect @AndroidEntryPoint class ProfileFragment : BaseFragment(R.layout.fragment_profile) { @@ -25,6 +30,20 @@ class ProfileFragment : BaseFragment(R.layout.fragment_profile) { viewBinding.logOutButton.setOnClickListener { viewModel.logout() } + viewModel.getProfileInformation() + + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.profileInformationStateFlow().collect { info -> + if (info != null) { + viewBinding.emailTextView.text = info.email + viewBinding.firstNameTextView.text = info.firstName + viewBinding.lastNameTextView.text = info.lastName + viewBinding.nickNameTextView.text = info.nickName + } + } + } + } } diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileViewModel.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileViewModel.kt index bcb82fe..fda21e6 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileViewModel.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/profile/ProfileViewModel.kt @@ -1,16 +1,33 @@ package com.andreyyurko.firstapp.ui.profile import androidx.lifecycle.viewModelScope +import com.andreyyurko.firstapp.entity.ProfileInformation import com.andreyyurko.firstapp.interactor.AuthInteractor +import com.andreyyurko.firstapp.interactor.ProfileInformationInteractor import com.andreyyurko.firstapp.ui.base.BaseViewModel +import com.andreyyurko.firstapp.ui.signin.SignInViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class ProfileViewModel @Inject constructor( - private val authInteractor: AuthInteractor + private val authInteractor: AuthInteractor, + private val profileInformationInteractor: ProfileInformationInteractor ) : BaseViewModel() { + private val profileInformationFlow = MutableStateFlow(null) + + fun getProfileInformation() { + viewModelScope.launch { + profileInformationFlow.emit(profileInformationInteractor.getProfileInformation().first()) + } + } + + fun profileInformationStateFlow(): Flow { + return profileInformationFlow.asStateFlow() + } + fun logout() { viewModelScope.launch { authInteractor.logout() diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInViewModel.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInViewModel.kt index 2b98e00..d122e35 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInViewModel.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/signin/SignInViewModel.kt @@ -3,6 +3,7 @@ package com.andreyyurko.firstapp.ui.signin import androidx.lifecycle.viewModelScope import com.andreyyurko.firstapp.data.network.response.error.SignInWithEmailErrorResponse import com.andreyyurko.firstapp.interactor.AuthInteractor +import com.andreyyurko.firstapp.interactor.ProfileInformationInteractor import com.andreyyurko.firstapp.ui.base.BaseViewModel import com.haroldadmin.cnradapter.NetworkResponse import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpViewModel.kt b/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpViewModel.kt index 4e5dab8..918aa3e 100644 --- a/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpViewModel.kt +++ b/app/src/main/java/com/andreyyurko/firstapp/ui/signup/SignUpViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.viewModelScope import com.andreyyurko.firstapp.data.network.response.error.CreateProfileErrorResponse import com.andreyyurko.firstapp.entity.ProfileInformation import com.andreyyurko.firstapp.interactor.AuthInteractor +import com.andreyyurko.firstapp.interactor.ProfileInformationInteractor import com.andreyyurko.firstapp.ui.base.BaseViewModel import com.haroldadmin.cnradapter.NetworkResponse import dagger.hilt.android.lifecycle.HiltViewModel @@ -16,7 +17,8 @@ import javax.inject.Inject @HiltViewModel class SignUpViewModel @Inject constructor( - private val authInteractor: AuthInteractor + private val authInteractor: AuthInteractor, + private val profileInformationInteractor: ProfileInformationInteractor ) : BaseViewModel() { private val _signUpActionStateFlow = MutableStateFlow(SignUpActionState.Loading) @@ -50,6 +52,13 @@ class SignUpViewModel @Inject constructor( password = password )) { is NetworkResponse.Success -> { + profileInformationInteractor.saveProfileInformation(ProfileInformation( + email = email, + firstName = firstName, + lastName = lastName, + nickName = nickName, + password = password + )) _signUpActionStateFlow.emit(SignUpActionState.SignUpSuccess) } is NetworkResponse.ServerError -> { diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml index 6946a76..c7d0e65 100644 --- a/app/src/main/res/layout/fragment_profile.xml +++ b/app/src/main/res/layout/fragment_profile.xml @@ -1,8 +1,9 @@ + android:layout_height="match_parent"> + + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + + + \ No newline at end of file