Skip to content

Commit 96731c4

Browse files
aleksandar-apostolovgpuntoCopilot
authored
Add connection recovery handler and lifecycle monitor (#28)
Co-authored-by: Gianmarco <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent cd0a959 commit 96731c4

File tree

43 files changed

+2722
-330
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2722
-330
lines changed

app/src/main/java/io/getstream/android/core/sample/SampleActivity.kt

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.fillMaxSize
2828
import androidx.compose.foundation.layout.padding
2929
import androidx.compose.foundation.rememberScrollState
3030
import androidx.compose.foundation.verticalScroll
31+
import androidx.compose.material3.Button
3132
import androidx.compose.material3.Scaffold
3233
import androidx.compose.material3.Text
3334
import androidx.compose.runtime.Composable
@@ -40,24 +41,38 @@ import androidx.lifecycle.lifecycleScope
4041
import androidx.lifecycle.repeatOnLifecycle
4142
import io.getstream.android.core.api.StreamClient
4243
import io.getstream.android.core.api.authentication.StreamTokenProvider
43-
import io.getstream.android.core.api.model.connection.network.StreamNetworkState
44+
import io.getstream.android.core.api.model.connection.StreamConnectionState
45+
import io.getstream.android.core.api.model.connection.recovery.Recovery
4446
import io.getstream.android.core.api.model.value.StreamApiKey
4547
import io.getstream.android.core.api.model.value.StreamHttpClientInfoHeader
4648
import io.getstream.android.core.api.model.value.StreamToken
4749
import io.getstream.android.core.api.model.value.StreamUserId
4850
import io.getstream.android.core.api.model.value.StreamWsUrl
51+
import io.getstream.android.core.api.socket.listeners.StreamClientListener
52+
import io.getstream.android.core.api.subscribe.StreamSubscription
53+
import io.getstream.android.core.api.subscribe.StreamSubscriptionManager
4954
import io.getstream.android.core.sample.client.createStreamClient
5055
import io.getstream.android.core.sample.ui.ConnectionStateCard
51-
import io.getstream.android.core.sample.ui.NetworkInfoCard
5256
import io.getstream.android.core.sample.ui.theme.StreamandroidcoreTheme
5357
import kotlinx.coroutines.launch
54-
import kotlinx.coroutines.runBlocking
5558

56-
class SampleActivity : ComponentActivity() {
59+
class SampleActivity : ComponentActivity(), StreamClientListener {
5760

5861
val userId = StreamUserId.fromString("petar")
5962
var streamClient: StreamClient? = null
6063

64+
var handle: StreamSubscription? = null
65+
66+
override fun onRecovery(recovery: Recovery) {
67+
super.onRecovery(recovery)
68+
Log.d("SampleActivity", "Recovery: $recovery")
69+
}
70+
71+
override fun onError(err: Throwable) {
72+
super.onError(err)
73+
Log.e("SampleActivity", "Error: $err")
74+
}
75+
6176
override fun onCreate(savedInstanceState: Bundle?) {
6277
super.onCreate(savedInstanceState)
6378
val streamClient2 =
@@ -91,7 +106,21 @@ class SampleActivity : ComponentActivity() {
91106
)
92107
streamClient = streamClient2
93108
lifecycleScope.launch {
94-
repeatOnLifecycle(Lifecycle.State.RESUMED) { streamClient?.connect() }
109+
repeatOnLifecycle(Lifecycle.State.CREATED) { streamClient?.connect() }
110+
}
111+
112+
if (handle == null) {
113+
handle =
114+
streamClient2
115+
.subscribe(
116+
this,
117+
options =
118+
StreamSubscriptionManager.Options(
119+
retention =
120+
StreamSubscriptionManager.Options.Retention.KEEP_UNTIL_CANCELLED
121+
),
122+
)
123+
.getOrThrow()
95124
}
96125
enableEdgeToEdge()
97126
setContent {
@@ -108,16 +137,43 @@ class SampleActivity : ComponentActivity() {
108137
) {
109138
Greeting(name = "Android")
110139
ClientInfo(streamClient = streamClient2)
140+
val state = streamClient?.connectionState?.collectAsStateWithLifecycle()
141+
val buttonState =
142+
when (state?.value) {
143+
is StreamConnectionState.Connected -> {
144+
Triple(
145+
"Disconnect",
146+
true,
147+
{
148+
lifecycleScope.launch { streamClient?.disconnect() }
149+
Unit
150+
},
151+
)
152+
}
153+
154+
is StreamConnectionState.Connecting -> {
155+
Triple("Connecting", false, { Unit })
156+
}
157+
158+
else -> {
159+
Triple(
160+
"Connect",
161+
true,
162+
{
163+
lifecycleScope.launch { streamClient?.connect() }
164+
Unit
165+
},
166+
)
167+
}
168+
}
169+
Button(onClick = buttonState.third, enabled = buttonState.second) {
170+
Text(text = buttonState.first)
171+
}
111172
}
112173
}
113174
}
114175
}
115176
}
116-
117-
override fun onStop() {
118-
runBlocking { streamClient?.disconnect() }
119-
super.onStop()
120-
}
121177
}
122178

123179
@Composable
@@ -134,18 +190,8 @@ fun GreetingPreview() {
134190
@Composable
135191
fun ClientInfo(streamClient: StreamClient) {
136192
val state = streamClient.connectionState.collectAsStateWithLifecycle()
137-
val networkSnapshot = streamClient.networkState.collectAsStateWithLifecycle()
138193
Log.d("SampleActivity", "Client state: ${state.value}")
139-
val networkState = networkSnapshot.value
140194
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
141195
ConnectionStateCard(state = state.value)
142-
when (networkState) {
143-
is StreamNetworkState.Available -> {
144-
NetworkInfoCard(snapshot = networkState.snapshot)
145-
}
146-
else -> {
147-
NetworkInfoCard(snapshot = null)
148-
}
149-
}
150196
}
151197
}

app/src/main/java/io/getstream/android/core/sample/client/StreamClient.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ import io.getstream.android.core.api.model.value.StreamApiKey
2828
import io.getstream.android.core.api.model.value.StreamHttpClientInfoHeader
2929
import io.getstream.android.core.api.model.value.StreamUserId
3030
import io.getstream.android.core.api.model.value.StreamWsUrl
31+
import io.getstream.android.core.api.observers.lifecycle.StreamLifecycleMonitor
3132
import io.getstream.android.core.api.observers.network.StreamNetworkMonitor
3233
import io.getstream.android.core.api.processing.StreamBatcher
3334
import io.getstream.android.core.api.processing.StreamRetryProcessor
3435
import io.getstream.android.core.api.processing.StreamSerialProcessingQueue
3536
import io.getstream.android.core.api.processing.StreamSingleFlightProcessor
37+
import io.getstream.android.core.api.recovery.StreamConnectionRecoveryEvaluator
3638
import io.getstream.android.core.api.serialization.StreamEventSerialization
3739
import io.getstream.android.core.api.socket.StreamConnectionIdHolder
3840
import io.getstream.android.core.api.socket.StreamWebSocketFactory
@@ -109,6 +111,15 @@ fun createStreamClient(
109111
logger = logProvider.taggedLogger("SCNetworkMonitorSubscriptions")
110112
),
111113
)
114+
val lifecycleMonitor =
115+
StreamLifecycleMonitor(
116+
logger = logProvider.taggedLogger("SCLifecycleMonitor"),
117+
subscriptionManager =
118+
StreamSubscriptionManager(
119+
logger = logProvider.taggedLogger("SCLifecycleMonitorSubscriptions")
120+
),
121+
lifecycle = androidComponentsProvider.lifecycle(),
122+
)
112123

113124
return StreamClient(
114125
scope = scope,
@@ -136,6 +147,12 @@ fun createStreamClient(
136147
override fun deserialize(raw: String): Result<Unit> = Result.success(Unit)
137148
}
138149
),
150+
lifecycleMonitor = lifecycleMonitor,
151+
connectionRecoveryEvaluator =
152+
StreamConnectionRecoveryEvaluator(
153+
logger = logProvider.taggedLogger("SCConnectionRecoveryEvaluator"),
154+
singleFlightProcessor = singleFlight,
155+
),
139156
batcher = batcher,
140157
)
141158
}

gradle/libs.versions.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ junitVersion = "1.3.0"
1111
espressoCore = "3.7.0"
1212
appcompat = "1.7.1"
1313
kotlinxCoroutines = "1.10.2"
14+
lifecycleRuntime = "2.9.4"
1415
lintApi = "31.12.0"
1516
material = "1.12.0"
1617
jetbrainsKotlinJvm = "2.2.0"
@@ -35,6 +36,8 @@ annotationJvm = "1.9.1"
3536
[libraries]
3637
androidx-core = { module = "androidx.test:core", version.ref = "core" }
3738
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
39+
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime", version.ref = "lifecycleRuntime" }
40+
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycleRuntime" }
3841
detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
3942
junit = { group = "junit", name = "junit", version.ref = "junit" }
4043
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }

gradle/scripts/sonar.gradle

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ ext.sonar.ignoreModules.each {
2222
ext.sonar.excludeFilter << "**/${it}/**"
2323
}
2424

25+
26+
def coverageReports = rootProject.subprojects
27+
.findAll { !rootProject.ext.sonar.ignoreModules.contains(it.name) }
28+
.collect { "${it.buildDir}/reports/kover/report.xml" }
29+
30+
if (coverageReports.isEmpty()) {
31+
coverageReports << "${rootProject.buildDir}/reports/kover/report.xml"
32+
}
33+
2534
sonarqube {
2635
properties {
2736
property("sonar.host.url", "https://sonarcloud.io")
@@ -32,6 +41,13 @@ sonarqube {
3241
property "sonar.java.coveragePlugin", "jacoco"
3342
property "sonar.sourceEncoding", "UTF-8"
3443
property "sonar.java.binaries", "${rootDir}/**/build/tmp/java-classes/debug"
44+
property "sonar.coverage.jacoco.xmlReportPaths", coverageReports.join(",")
3545
property "sonar.coverage.exclusions", rootProject.ext.sonar.excludeFilter
3646
}
3747
}
48+
49+
tasks.named("sonar").configure {
50+
rootProject.subprojects
51+
.findAll { !rootProject.ext.sonar.ignoreModules.contains(it.name) }
52+
.each { dependsOn("${it.path}:koverXmlReport") }
53+
}

stream-android-core/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ dependencies {
6565

6666
// Android
6767
implementation(libs.androidx.annotation.jvm)
68+
implementation(libs.androidx.lifecycle.runtime)
69+
implementation(libs.androidx.lifecycle.process)
6870

6971
// Network
7072
implementation(libs.moshi)

stream-android-core/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@
1818

1919
<uses-permission android:name="android.permission.INTERNET" />
2020
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
21+
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
2122

2223
</manifest>

0 commit comments

Comments
 (0)