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..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
@@ -18,10 +19,18 @@ 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
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/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..b9d4f90
--- /dev/null
+++ b/app/src/main/java/com/andreyyurko/firstapp/data/persistent/ProfileInformationStorage.kt
@@ -0,0 +1,51 @@
+package com.andreyyurko.firstapp.data.persistent
+
+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/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/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/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 655ca4b..09ff4d5 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
@@ -32,59 +31,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: обработать ошибки
+ }
}
}
}
@@ -92,17 +78,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: обработать случаи ошибки
}
}
}
@@ -111,13 +99,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 866a1d7..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
@@ -28,9 +28,20 @@ class OnboardingFragment : BaseFragment(R.layout.fragment_onboarding) {
private var isVolumeOn: Boolean = true
private var page: Int = 0
- private lateinit var timer: CountDownTimer
- private var millisRunning: Long = 4000
- private var countDownInterval: Long = 4000
+ private var isScroll = false
+ private var timerIsStop = false
+ 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)
@@ -40,19 +51,32 @@ 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()
}
- initViewPager()
+ viewBinding.viewPager.setTextPages()
+ viewBinding.viewPager.attachDots(viewBinding.onboardingTextTabLayout)
+ viewBinding.viewPager.offscreenPageLimit = 1
+ val nextItemVisiblePx = 250
+ val currentItemHorizontalMarginPx = 150
+ val pageTranslationX = nextItemVisiblePx + currentItemHorizontalMarginPx
+ 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))
+ // If you want a fading effect uncomment the next line:
+ page.alpha = 0.25f + (1 - abs(position))
+ }
+ viewBinding.viewPager.setPageTransformer(pageTransformer)
+
+// The ItemDecoration gives the current (centered) item horizontal margin so that
+// it doesn't occupy the whole screen width. Without it the items overlap
+ val itemDecoration = HorizontalMarginItemDecoration(
+ 200
+ )
+ viewBinding.viewPager.addItemDecoration(itemDecoration)
viewBinding.signInButton.setOnClickListener {
//Toast.makeText(requireContext(), "Нажата кнопка войти", Toast.LENGTH_SHORT).show()
@@ -72,13 +96,13 @@ class OnboardingFragment : BaseFragment(R.layout.fragment_onboarding) {
override fun onPause() {
super.onPause()
- timer.cancel()
+ timerIsStop = true
player?.pause()
}
override fun onDestroy() {
super.onDestroy()
- timer.cancel()
+ timerIsStop = true
player?.release()
}
@@ -98,75 +122,55 @@ class OnboardingFragment : BaseFragment(R.layout.fragment_onboarding) {
TabLayoutMediator(tabLayout, this) { _, _ -> }.attach()
}
- private fun initViewPager() {
- viewBinding.viewPager.setTextPages()
- viewBinding.viewPager.attachDots(viewBinding.onboardingTextTabLayout)
- viewBinding.viewPager.offscreenPageLimit = 1
- val nextItemVisiblePx = 250
- val currentItemHorizontalMarginPx = 150
- val pageTranslationX = nextItemVisiblePx + currentItemHorizontalMarginPx
- val pageTransformer = ViewPager2.PageTransformer { page: View, position: Float ->
- page.translationX = -pageTranslationX * position
- page.scaleY = 1 - (0.30f * abs(position))
- page.alpha = 0.25f + (1 - abs(position))
- }
- viewBinding.viewPager.setPageTransformer(pageTransformer)
-
- val itemDecoration = HorizontalMarginItemDecoration(
- 200
- )
- viewBinding.viewPager.addItemDecoration(itemDecoration)
- }
-
private fun changeVolume() {
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)
}
}
- private fun initTimer() {
- timer = object: CountDownTimer(millisRunning, countDownInterval) {
+ private fun timer() : CountDownTimer {
+ return object: CountDownTimer(millisRunning,countDownInterval) {
override fun onTick(millisUntilFinished: Long) {
- //Toast.makeText(requireContext(), "$millisUntilFinished", Toast.LENGTH_SHORT).show()
+ if (isScroll) {
+ cancel()
+ isScroll = false
+ timer().start()
+ }
+ if (timerIsStop) {
+ cancel()
+ }
}
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)
}
}
-
}
private fun autoscroll() {
- timer.start()
+ timerIsStop = false
+ timer().start()
viewBinding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
+ isScroll = true
page = position
- timer.cancel()
- timer.start()
}
})
}
-
- private fun initPlayer() {
- player = SimpleExoPlayer.Builder(requireContext()).build().apply {
- addMediaItem(MediaItem.fromUri("asset:///onboarding.mp4"))
- repeatMode = Player.REPEAT_MODE_ALL
- prepare()
- }
- viewBinding.playerView.player = player
- }
}
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..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
@@ -1,13 +1,51 @@
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) {
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()
+ }
+ 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
+ }
+ }
+ }
+ }
+
+ }
+
+
}
\ 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..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,7 +1,36 @@
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
-class ProfileViewModel : BaseViewModel(){
+@HiltViewModel
+class ProfileViewModel @Inject constructor(
+ 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()
+ }
+ }
+}
\ 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 9f9e98e..c74afd5 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
@@ -13,6 +17,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding
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) {
@@ -22,6 +27,7 @@ class SignInFragment : BaseFragment(R.layout.fragment_sign_in) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+
requireActivity().onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
@@ -34,6 +40,14 @@ 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() }
+ }
+ viewBinding.signInButton.applyInsetter {
+ type(navigationBars = true) { margin() }
+ }
viewBinding.backButton.setOnClickListener {
onBackButtonPressed()
}
@@ -65,6 +79,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()
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/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..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
@@ -1,8 +1,10 @@
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.interactor.ProfileInformationInteractor
import com.andreyyurko.firstapp.ui.base.BaseViewModel
import com.haroldadmin.cnradapter.NetworkResponse
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -15,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)
@@ -26,28 +29,37 @@ 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)
+ profileInformationInteractor.saveProfileInformation(ProfileInformation(
+ email = email,
+ firstName = firstName,
+ lastName = lastName,
+ nickName = nickName,
+ password = password
+ ))
+ _signUpActionStateFlow.emit(SignUpActionState.SignUpSuccess)
}
is NetworkResponse.ServerError -> {
_signUpActionStateFlow.emit(SignUpActionState.ServerError(response))
@@ -67,10 +79,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..c7d0e65 100644
--- a/app/src/main/res/layout/fragment_profile.xml
+++ b/app/src/main/res/layout/fragment_profile.xml
@@ -1,15 +1,71 @@
+ android:layout_height="match_parent">
+
+
+
+
+
+
+
+
+
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:text="News"
+ android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/firstNameTextView" />
+
+
\ 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