Skip to content

Commit dbf385c

Browse files
authored
Merge pull request #9821 from wordpress-mobile/issue/9568-connectionstatuslivedata-revamp
Make ConnectionStatusLiveData distinct
2 parents 1a4f71f + e452be5 commit dbf385c

File tree

4 files changed

+119
-10
lines changed

4 files changed

+119
-10
lines changed

WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public static WizardManager<SiteCreationStep> provideWizardManager(NewSiteCreati
5454
}
5555

5656
@Provides
57-
public static LiveData<ConnectionStatus> provideConnectionStatusLiveData(Context context) {
58-
return new ConnectionStatusLiveData(context);
57+
static LiveData<ConnectionStatus> provideConnectionStatusLiveData(Context context) {
58+
return new ConnectionStatusLiveData.Factory(context).create();
5959
}
6060
}

WordPress/src/main/java/org/wordpress/android/viewmodel/helpers/ConnectionStatusLiveData.kt

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,26 @@ import android.content.Context
66
import android.content.Intent
77
import android.content.IntentFilter
88
import android.net.ConnectivityManager
9+
import org.wordpress.android.util.distinct
10+
import org.wordpress.android.viewmodel.helpers.ConnectionStatus.AVAILABLE
11+
import org.wordpress.android.viewmodel.helpers.ConnectionStatus.UNAVAILABLE
12+
import org.wordpress.android.viewmodel.helpers.ConnectionStatusLiveData.Factory
13+
import javax.inject.Inject
914

10-
/**
11-
* A wrapper class for the network connection status. It can be extended to provide more details about the current
12-
* network connection.
13-
*/
14-
class ConnectionStatus(val isConnected: Boolean)
15+
enum class ConnectionStatus {
16+
AVAILABLE,
17+
UNAVAILABLE
18+
}
1519

1620
/**
1721
* A LiveData instance that can be injected to keep track of the network availability.
1822
*
23+
* Use [Factory] to create an instance. The Factory guarantees that this only emits if the network availability
24+
* changes and not when the user switches between cellular and wi-fi.
25+
*
1926
* IMPORTANT: It needs to be observed for the changes to be posted.
2027
*/
21-
class ConnectionStatusLiveData(private val context: Context) : LiveData<ConnectionStatus>() {
28+
class ConnectionStatusLiveData private constructor(private val context: Context) : LiveData<ConnectionStatus>() {
2229
override fun onActive() {
2330
super.onActive()
2431
val intentFilter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
@@ -34,7 +41,13 @@ class ConnectionStatusLiveData(private val context: Context) : LiveData<Connecti
3441
override fun onReceive(context: Context, intent: Intent) {
3542
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
3643
val networkInfo = connectivityManager?.activeNetworkInfo
37-
postValue(ConnectionStatus(networkInfo?.isConnected == true))
44+
45+
val nextValue: ConnectionStatus = if (networkInfo?.isConnected == true) AVAILABLE else UNAVAILABLE
46+
postValue(nextValue)
3847
}
3948
}
49+
50+
class Factory @Inject constructor(private val context: Context) {
51+
fun create() = ConnectionStatusLiveData(context).distinct()
52+
}
4053
}

WordPress/src/main/java/org/wordpress/android/viewmodel/history/HistoryViewModel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import org.wordpress.android.util.NetworkUtilsWrapper
3232
import org.wordpress.android.viewmodel.ResourceProvider
3333
import org.wordpress.android.viewmodel.SingleLiveEvent
3434
import org.wordpress.android.viewmodel.helpers.ConnectionStatus
35+
import org.wordpress.android.viewmodel.helpers.ConnectionStatus.AVAILABLE
3536
import javax.inject.Inject
3637
import javax.inject.Named
3738

@@ -74,7 +75,7 @@ class HistoryViewModel @Inject constructor(
7475
lifecycleRegistry.markState(Lifecycle.State.CREATED)
7576
dispatcher.register(this)
7677
connectionStatus.observe(this, Observer {
77-
if (it?.isConnected == true) {
78+
if (it == AVAILABLE) {
7879
fetchRevisions()
7980
}
8081
})
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package org.wordpress.android.viewmodel.helpers
2+
3+
import android.arch.core.executor.testing.InstantTaskExecutorRule
4+
import android.arch.lifecycle.LiveData
5+
import android.content.BroadcastReceiver
6+
import android.content.Context
7+
import android.net.ConnectivityManager
8+
import android.net.NetworkInfo
9+
import com.nhaarman.mockitokotlin2.any
10+
import com.nhaarman.mockitokotlin2.argumentCaptor
11+
import com.nhaarman.mockitokotlin2.doReturn
12+
import com.nhaarman.mockitokotlin2.mock
13+
import org.assertj.core.api.Assertions.assertThat
14+
import org.junit.Before
15+
import org.junit.Rule
16+
import org.junit.Test
17+
import org.wordpress.android.viewmodel.helpers.ConnectionStatus.AVAILABLE
18+
import org.wordpress.android.viewmodel.helpers.ConnectionStatus.UNAVAILABLE
19+
20+
class ConnectionStatusLiveDataTest {
21+
@get:Rule val rule = InstantTaskExecutorRule()
22+
23+
private lateinit var connectionStatusLiveData: LiveData<ConnectionStatus>
24+
private lateinit var broadcastReceiver: BroadcastReceiver
25+
26+
@Before
27+
fun setUp() {
28+
val captor = argumentCaptor<BroadcastReceiver>()
29+
val context = mock<Context> {
30+
on { registerReceiver(captor.capture(), any()) } doReturn mock()
31+
}
32+
33+
connectionStatusLiveData = ConnectionStatusLiveData.Factory(context).create()
34+
// Start observing to capture the broadcastReceiver
35+
connectionStatusLiveData.observeForever { }
36+
37+
broadcastReceiver = captor.firstValue
38+
}
39+
40+
@Test
41+
fun `it emits a value when receiving a network info change`() {
42+
assertThat(connectionStatusLiveData.value).isNull()
43+
44+
broadcastReceiver.onReceive(mockedBroadcastReceiverContext(connectedNetwork = false), mock())
45+
46+
assertThat(connectionStatusLiveData.value).isEqualTo(UNAVAILABLE)
47+
}
48+
49+
@Test
50+
fun `it emits a value when the network availability changes`() {
51+
// Arrange
52+
broadcastReceiver.onReceive(mockedBroadcastReceiverContext(connectedNetwork = true), mock())
53+
assertThat(connectionStatusLiveData.value).isEqualTo(AVAILABLE)
54+
55+
// Act
56+
broadcastReceiver.onReceive(mockedBroadcastReceiverContext(connectedNetwork = false), mock())
57+
58+
// Assert
59+
assertThat(connectionStatusLiveData.value).isEqualTo(UNAVAILABLE)
60+
}
61+
62+
@Test
63+
fun `it does not emit a value when the network available didn't change`() {
64+
// Arrange
65+
var emitCount = 0
66+
67+
connectionStatusLiveData.observeForever {
68+
emitCount += 1
69+
}
70+
71+
broadcastReceiver.onReceive(mockedBroadcastReceiverContext(connectedNetwork = true), mock())
72+
73+
// Act
74+
repeat(3) {
75+
broadcastReceiver.onReceive(mockedBroadcastReceiverContext(connectedNetwork = true), mock())
76+
}
77+
78+
// Assert
79+
assertThat(emitCount).isEqualTo(1)
80+
assertThat(connectionStatusLiveData.value).isEqualTo(AVAILABLE)
81+
}
82+
83+
private fun mockedBroadcastReceiverContext(connectedNetwork: Boolean): Context {
84+
val networkInfo = mock<NetworkInfo> {
85+
on { isConnected } doReturn connectedNetwork
86+
}
87+
val connectivityManager = mock<ConnectivityManager> {
88+
on { activeNetworkInfo } doReturn networkInfo
89+
}
90+
91+
return mock {
92+
on { getSystemService(any()) } doReturn connectivityManager
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)