google-developer-training / advanced-android-kotlin-login-navigation

Other
46 stars 33 forks source link

Advanced Android in Kotlin: Login 6.2 [Errors in Template Code] #9

Open yingding opened 4 years ago

yingding commented 4 years ago

Describe the problem

  1. LoginViewModel#getFactToDisplay contains error:
    fun getFactToDisplay(context: Context): String {
       ...
        if (authenticationState == AuthenticationState.UNAUTHENTICATED || funFactType.equals(
                context.getString(R.string.fact_type_android)
            )
        ) {
            return androidFacts[Random.nextInt(0, androidFacts.size)]
        } else {
            return californiaFacts[Random.nextInt(0, californiaFacts.size)]
        }
    }

since authenticationState is a LiveData<Enum> , the value property shall be used to compare with enum otherwise code will not compile.

    fun getFactToDisplay(context: Context): String {
       ...
        if (authenticationState.value == AuthenticationState.UNAUTHENTICATED || funFactType.equals(
                context.getString(R.string.fact_type_android)
            )
        ) {
            return androidFacts[Random.nextInt(0, androidFacts.size)]
        } else {
            return californiaFacts[Random.nextInt(0, californiaFacts.size)]
        }
    }
  1. MainFragment#observeAuthenticationState contains logical error

    private fun observeAuthenticationState() {
        val factToDisplay = viewModel.getFactToDisplay(requireContext())
    
        viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
            when (authenticationState) {
                LoginViewModel.AuthenticationState.AUTHENTICATED -> {
                    binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
    
                    binding.authButton.text = getString(R.string.logout_button_text)
                    binding.authButton.setOnClickListener {
                        AuthUI.getInstance().signOut(requireContext())
                    }
                }
                else -> {
                    binding.welcomeText.text = factToDisplay
    
                    binding.authButton.text = getString(R.string.login_button_text)
                    binding.authButton.setOnClickListener {
                        findNavController().navigate(R.id.loginFragment)
                    }
                }
            }
        })
    }

    viewModel.getFactToDisplay is called before the viewModel.authenticationState.observe call intends to return either an androidFact or a californiaFact. Unfortunately, the FirebaseUserLiveData().map function assigning LoginViewModel#authenticationState is only executed when the LoginViewModel is observed. For this reason, authenticationState.value returns null and viewModel.getFactToDisplay returns a california fact instead of the intended android fact by default.

A possible solution is to move the initialization of factToDisplay inside the observe statement

    private fun observeAuthenticationState() {
        // val factToDisplay = viewModel.getFactToDisplay(requireContext())

        viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
            // move inside observer statement so the the FirebaseUserLiveData().map function in LoginViewModel can be executed.
            val factToDisplay = viewModel.getFactToDisplay(requireContext())
            when (authenticationState) {
                LoginViewModel.AuthenticationState.AUTHENTICATED -> {
                    binding.welcomeText.text = getFactWithPersonalization(factToDisplay)

                    binding.authButton.text = getString(R.string.logout_button_text)
                    binding.authButton.setOnClickListener {
                        AuthUI.getInstance().signOut(requireContext())
                    }
                }
                else -> {
                    binding.welcomeText.text = factToDisplay

                    binding.authButton.text = getString(R.string.login_button_text)
                    binding.authButton.setOnClickListener {
                        findNavController().navigate(R.id.loginFragment)
                    }
                }
            }
        })
    }

How to reproduce? What are the exact steps to reproduce the problem? Problem 1. Compile the solution project from Master branch, a compiler error will be shown. Problem 2. Run the compiled solution project from Master branch, a california fact is displayed by default instead of an android fact.

Versions

  1. What version of Android Studio are you using? Android Studio 4.0.1

  2. What API level are you targeting? API 29 , API 30

Additional information Compile error, and Califonia Fact is shown when not logged in or after log out

codelab: advanced-android-kotlin

hhyeok1026 commented 2 years ago

it helped me thank you