Skip to content

Commit

Permalink
Disable sign-in button & show error message when network is unavailab…
Browse files Browse the repository at this point in the history
…le (#2067)
  • Loading branch information
shobhitagarwal1612 authored Nov 13, 2023
1 parent cfab823 commit f3dba6f
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ enum class NetworkStatus {
class NetworkManager @Inject constructor(@ApplicationContext private val context: Context) {
val networkStatusFlow = initNetworkStatusFlow()

fun initNetworkStatusFlow(): Flow<NetworkStatus> {
private fun initNetworkStatusFlow(): Flow<NetworkStatus> {
val connectivityManager = context.getSystemService(ConnectivityManager::class.java)
return callbackFlow {
val callback =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,20 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.google.android.ground.R
import com.google.android.ground.databinding.SignInFragBinding
import com.google.android.ground.ui.common.AbstractFragment
import com.google.android.ground.ui.common.BackPressListener
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

@AndroidEntryPoint(AbstractFragment::class)
class SignInFragment : Hilt_SignInFragment(), BackPressListener {

private lateinit var binding: SignInFragBinding
private lateinit var viewModel: SignInViewModel

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -39,12 +45,34 @@ class SignInFragment : Hilt_SignInFragment(), BackPressListener {
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = SignInFragBinding.inflate(inflater, container, false)
binding = SignInFragBinding.inflate(inflater, container, false)
binding.viewModel = viewModel
binding.lifecycleOwner = this
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

lifecycleScope.launch(Dispatchers.Main) {
viewModel.getNetworkFlow().collect { connected ->
if (!connected) {
displayNetworkError()
}
binding.signInButton.isEnabled = connected
}
}
}

private fun displayNetworkError() {
Snackbar.make(
requireView(),
getString(R.string.network_error_when_signing_in),
Snackbar.LENGTH_LONG
)
.show()
}

override fun onBack(): Boolean {
// Workaround to exit on back from sign-in screen since for some reason
// popUpTo is not working on signOut action.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,31 @@
*/
package com.google.android.ground.ui.signin

import androidx.lifecycle.viewModelScope
import com.google.android.ground.repository.UserRepository
import com.google.android.ground.system.NetworkManager
import com.google.android.ground.system.NetworkStatus
import com.google.android.ground.ui.common.AbstractViewModel
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.shareIn

class SignInViewModel
@Inject
internal constructor(
private val networkManager: NetworkManager,
private val userRepository: UserRepository
) : AbstractViewModel() {

@OptIn(ExperimentalCoroutinesApi::class)
fun getNetworkFlow(): Flow<Boolean> =
networkManager.networkStatusFlow
.mapLatest { it == NetworkStatus.AVAILABLE }
.shareIn(viewModelScope, SharingStarted.Lazily, replay = 0)

class SignInViewModel @Inject internal constructor(private val userRepository: UserRepository) :
AbstractViewModel() {
fun onSignInButtonClick() {
userRepository.signIn()
}
Expand Down
1 change: 1 addition & 0 deletions ground/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,5 @@
<string name="offline_area_list_item_icon">Offline area list item icon</string>
<string name="no_surveys_available">No surveys available. Contact your survey organizer to get access.</string>
<string name="previous">Previous</string>
<string name="network_error_when_signing_in">Connect to the internet to sign in</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,37 @@ package com.google.android.ground.ui.signin

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isNotEnabled
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.google.android.ground.BaseHiltTest
import com.google.android.ground.R
import com.google.android.ground.launchFragmentInHiltContainer
import com.google.android.ground.system.NetworkManager
import com.google.android.ground.system.NetworkStatus
import com.google.android.ground.system.auth.SignInState
import com.google.common.truth.Truth.assertThat
import com.sharedtest.FakeData
import com.sharedtest.system.auth.FakeAuthenticationManager
import dagger.hilt.android.testing.BindValue
import dagger.hilt.android.testing.HiltAndroidTest
import javax.inject.Inject
import kotlinx.coroutines.flow.flowOf
import org.hamcrest.Matchers.allOf
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.kotlin.whenever
import org.robolectric.RobolectricTestRunner

@HiltAndroidTest
@RunWith(RobolectricTestRunner::class)
class SignInFragmentTest : BaseHiltTest() {

@BindValue @Mock lateinit var networkManager: NetworkManager

@Inject lateinit var fakeAuthenticationManager: FakeAuthenticationManager

override fun setUp() {
Expand All @@ -43,7 +56,8 @@ class SignInFragmentTest : BaseHiltTest() {
}

@Test
fun buttonClick_shouldSignIn() {
fun `Clicking sign-in button when network is available should attempt login`() {
whenever(networkManager.networkStatusFlow).thenReturn(flowOf(NetworkStatus.AVAILABLE))
launchFragmentInHiltContainer<SignInFragment>()
fakeAuthenticationManager.setUser(TEST_USER)

Expand All @@ -55,7 +69,24 @@ class SignInFragmentTest : BaseHiltTest() {
}

@Test
fun pressBack_shouldFinishActivity() {
fun `Sign-in button should be disabled when network is not available`() {
whenever(networkManager.networkStatusFlow).thenReturn(flowOf(NetworkStatus.UNAVAILABLE))
launchFragmentInHiltContainer<SignInFragment>()
fakeAuthenticationManager.setUser(TEST_USER)

onView(allOf(withId(R.id.sign_in_button), isDisplayed(), isNotEnabled())).perform(click())

// Assert that the sign-in state is still signed out
fakeAuthenticationManager.signInState
.test()
.assertValue(SignInState(SignInState.State.SIGNED_OUT, Result.success(null)))

onView(withId(com.google.android.material.R.id.snackbar_text))
.check(matches(withText(R.string.network_error_when_signing_in)))
}

@Test
fun `Back press should finish activity`() {
launchFragmentInHiltContainer<SignInFragment> {
val fragment = this as SignInFragment
assertThat(fragment.onBack()).isFalse()
Expand Down

0 comments on commit f3dba6f

Please sign in to comment.