Skip to content

Commit

Permalink
Make the AuthActivity more robust by handling all the errors and ask …
Browse files Browse the repository at this point in the history
…for location permission in HomeFragment
  • Loading branch information
andraantariksa committed Apr 12, 2022
1 parent 4273519 commit 6d3e88f
Show file tree
Hide file tree
Showing 40 changed files with 1,146 additions and 367 deletions.
5 changes: 3 additions & 2 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Koffie.SplashScreen"
tools:replace="android:allowBackup">
<activity
android:name=".ui.main.MainActivity"
android:theme="@style/Theme.Koffie.SplashScreen"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -29,10 +29,8 @@
</activity>
<activity
android:name=".ui.auth.AuthActivity"
android:theme="@style/Theme.Koffie"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
</application>

Expand Down
24 changes: 24 additions & 0 deletions app/src/main/kotlin/id/shaderboi/koffie/ui/auth/AuthActivity.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package id.shaderboi.koffie.ui.auth

import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.NavHostFragment
import dagger.hilt.android.AndroidEntryPoint
import id.shaderboi.koffie.databinding.ActivityAuthBinding
import id.shaderboi.koffie.ui.auth.signin.SignInFragmentDirections
import id.shaderboi.koffie.ui.common.view_model.AuthViewModel
import id.shaderboi.koffie.ui.main.MainActivity
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

Expand All @@ -22,6 +26,26 @@ class AuthActivity : AppCompatActivity() {

_binding = ActivityAuthBinding.inflate(layoutInflater)

setupAuthentication()

setContentView(binding.root)
}

private fun setupAuthentication() {
val navHostFragment = binding.fragmentContainerViewMain.getFragment<NavHostFragment>()
val navController = navHostFragment.navController

authViewModel.authenticationFlow.onEach { user ->
if (user?.isRegistered == false) {
val action = SignInFragmentDirections
.actionNavigationAuthSigninToNavigationAuthRegistration()
navController.navigate(action)
} else if (user !== null) {
val intent = Intent(this, MainActivity::class.java)
intent.flags =
Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
}.launchIn(lifecycleScope)
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
package id.shaderboi.koffie.ui.auth.registration

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import id.shaderboi.koffie.databinding.FragmentRegistrationBinding
import id.shaderboi.koffie.ui.common.view_model.AuthEvent
import id.shaderboi.koffie.ui.common.view_model.AuthViewModel
import id.shaderboi.koffie.ui.auth.registration.view_model.RegistrationEvent
import id.shaderboi.koffie.ui.auth.registration.view_model.RegistrationUIEvent
import id.shaderboi.koffie.ui.auth.registration.view_model.RegistrationViewModel
import id.shaderboi.koffie.ui.main.MainActivity
import id.shaderboi.koffie.util.StringDisplay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

@AndroidEntryPoint
class RegistrationFragment : Fragment() {
private var _binding: FragmentRegistrationBinding? = null
val binding get() = _binding!!

private val signInViewModel by activityViewModels<AuthViewModel>()
private val registrationViewModel by viewModels<RegistrationViewModel>()

override fun onCreateView(
inflater: LayoutInflater,
Expand All @@ -26,17 +36,61 @@ class RegistrationFragment : Fragment() {
): View {
_binding = FragmentRegistrationBinding.inflate(inflater, container, false)

collectUIEvent()
setupView()

return binding.root
}

private fun collectUIEvent() {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
registrationViewModel.uiFlow.collectLatest { event ->
when (event) {
RegistrationUIEvent.Finished -> {
val intent = Intent(requireContext(), MainActivity::class.java)
intent.flags =
Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
intent.putExtra(IntentExtra.WITH_SPLASH_SCREEN, false)
startActivity(intent)
}
is RegistrationUIEvent.ShowErrorMessage -> {
binding.textViewErrorMessage.isVisible = true
binding.textViewErrorMessage.text = when (event.message) {
is StringDisplay.String -> event.message.string
is StringDisplay.StringRes -> requireContext().getString(event.message.idRes)
}
}
is RegistrationUIEvent.IsEnableInput -> event.enabled.let { enabled ->
if (!enabled) {
binding.textViewErrorMessage.isVisible = false
}

binding.editTextName.isEnabled = enabled
binding.editTextBirthDate.isEnabled = enabled
binding.buttonSubmit.isEnabled = enabled
}
}
}
}
}
}

private fun setupView() {
val navController = binding.root.findNavController()
val datePicker = MaterialDatePicker.Builder
.datePicker()
.build()

binding.editTextBirthDate.setOnClickListener {
datePicker.show(parentFragmentManager, null)
}

binding.buttonSubmit.setOnClickListener {
val displayName = binding.editTextName.text.toString()
val birthDate = binding.editTextBirthDate.text.toString()
val selectedGender = binding.spinnerGender.selectedItemPosition

binding.buttonSignInSignUp.setOnClickListener {
val phoneNumber = binding.editTextVerificationCode1.text.toString()
signInViewModel.onEvent(AuthEvent.Verify(phoneNumber, requireActivity(), navController))
registrationViewModel.onEvent(RegistrationEvent.Register(displayName))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package id.shaderboi.koffie.ui.auth.registration.view_model

import android.app.Activity
import com.google.firebase.auth.AuthResult

sealed class RegistrationEvent {
class Register(val displayName: String) : RegistrationEvent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package id.shaderboi.koffie.ui.auth.registration.view_model

import id.shaderboi.koffie.util.StringDisplay

sealed class RegistrationUIEvent {
class ShowErrorMessage(val message: StringDisplay): RegistrationUIEvent()
class IsEnableInput(val enabled: Boolean) : RegistrationUIEvent()
object Finished: RegistrationUIEvent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package id.shaderboi.koffie.ui.auth.registration.view_model

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.firebase.auth.FirebaseAuthException
import com.google.firebase.auth.FirebaseAuthInvalidUserException
import dagger.hilt.android.lifecycle.HiltViewModel
import id.shaderboi.koffie.core.data.auth.Auth
import id.shaderboi.koffie.util.StringDisplay
import id.shaderboi.koffie.util.firebase.auth.errorCodeResId
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class RegistrationViewModel @Inject constructor(
private val auth: Auth
) : ViewModel() {
private val _uiFlow = MutableSharedFlow<RegistrationUIEvent>()
val uiFlow = _uiFlow.asSharedFlow()

fun onEvent(event: RegistrationEvent) {
when (event) {
is RegistrationEvent.Register -> {
register(event.displayName)
}
}
}

private fun register(displayName: String) = viewModelScope.launch {
_uiFlow.emit(RegistrationUIEvent.IsEnableInput(false))
auth.register(displayName)
.catch { err ->
val message = when(err) {
is FirebaseAuthException -> err.errorCodeResId?.let { resId ->
StringDisplay.StringRes(resId)
} ?: StringDisplay.String("Uknown error. ${err.message}")
else -> StringDisplay.String("Uknown error. ${err.message}")
}
_uiFlow.emit(RegistrationUIEvent.ShowErrorMessage(message))
_uiFlow.emit(RegistrationUIEvent.IsEnableInput(true))
}
.collectLatest {
_uiFlow.emit(RegistrationUIEvent.Finished)
_uiFlow.emit(RegistrationUIEvent.IsEnableInput(true))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,28 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import id.shaderboi.koffie.databinding.FragmentSigninBinding
import id.shaderboi.koffie.ui.common.view_model.AuthEvent
import id.shaderboi.koffie.ui.common.view_model.AuthViewModel
import id.shaderboi.koffie.ui.auth.signin.view_model.SignInEvent
import id.shaderboi.koffie.ui.auth.signin.view_model.SignInUIEvent
import id.shaderboi.koffie.ui.auth.signin.view_model.SignInViewModel
import id.shaderboi.koffie.util.StringDisplay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

@AndroidEntryPoint
class SignInFragment : Fragment() {
private var _binding: FragmentSigninBinding? = null
val binding get() = _binding!!

private val signInViewModel by activityViewModels<AuthViewModel>()
private val signInViewModel by viewModels<SignInViewModel>()

override fun onCreateView(
inflater: LayoutInflater,
Expand All @@ -26,25 +34,51 @@ class SignInFragment : Fragment() {
): View {
_binding = FragmentSigninBinding.inflate(inflater)

collectUIEvent()
setupView()

return binding.root
}

private fun setupView() {
val navController = findNavController()
binding.buttonSignInSignUp.setOnClickListener {
val phoneNumber = binding.editTextPhoneNumber.text.toString()
signInViewModel.onEvent(
AuthEvent.Auth(
phoneNumber,
requireActivity()
) {
val action =
SignInFragmentDirections.actionNavigationAuthSigninToNavigationAuthVerify()
navController.navigate(action)
private fun collectUIEvent() {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
signInViewModel.uiFlow.collectLatest { event ->
when (event) {
is SignInUIEvent.ShowErrorMessage -> {
binding.textViewErrorMessage.isVisible = true
binding.textViewErrorMessage.text = when (event.message) {
is StringDisplay.String -> event.message.string
is StringDisplay.StringRes -> requireContext().getString(event.message.idRes)
}
}
is SignInUIEvent.Finished -> {
val navController = findNavController()
val action = SignInFragmentDirections
.actionNavigationAuthSigninToNavigationAuthVerify(
event.verificationId
)
navController.navigate(action)
}
is SignInUIEvent.IsEnableInput -> event.enable.let { enable ->
if (!enable) {
binding.textViewErrorMessage.isVisible = false
}

binding.buttonSigninSignup.isEnabled = enable
binding.editTextPhoneNumber.isEnabled = enable
}
}
}
)
}
}
}

private fun setupView() {
binding.buttonSigninSignup.setOnClickListener {
val phoneNumberText = binding.editTextPhoneNumber.text.toString()
val phoneNumber = "+62${phoneNumberText}"
signInViewModel.onEvent(SignInEvent.SignIn(phoneNumber, requireActivity()))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package id.shaderboi.koffie.ui.auth.signin.view_model

import android.app.Activity

sealed class SignInEvent {
class SignIn(
val phoneNumber: String,
val activity: Activity
) : SignInEvent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package id.shaderboi.koffie.ui.auth.signin.view_model

import id.shaderboi.koffie.util.StringDisplay

sealed class SignInUIEvent {
class ShowErrorMessage(val message: StringDisplay): SignInUIEvent()
class Finished(val verificationId: String): SignInUIEvent()
class IsEnableInput(val enable: Boolean): SignInUIEvent()
}
Loading

0 comments on commit 6d3e88f

Please sign in to comment.