Skip to content

Commit ebcdea7

Browse files
committed
Merge branch 'develop'
2 parents 5428fcc + 80da737 commit ebcdea7

File tree

39 files changed

+829
-242
lines changed

39 files changed

+829
-242
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ Video roadmap and changelog is available [here](https://github.com/GetStream/pro
102102

103103
### 0.3.0 milestone
104104

105-
- [ ] Finish usability testing with design team on chat integration (Jaewoong)
105+
- [X] Finish usability testing with design team on chat integration (Jaewoong)
106106
- [X] Pagination on query members & query call endpoints (Daniel)
107107
- [X] Livestream tutorial (depends on RTMP support) (Thierry)
108108
- [X] local version of audioLevel(s) for lower latency audio visualizations(Daniel)
@@ -115,6 +115,7 @@ Video roadmap and changelog is available [here](https://github.com/GetStream/pro
115115

116116
### 0.4.0 milestone
117117

118+
- [ ] Complete Livestreaming APIs and Tutorials for hosting & watching
118119
- [ ] Android SDK development.md cleanup (Daniel)
119120
- [ ] Upgrade to more recent versions of webrtc (Kanat)
120121
- [ ] Picture of the video stream at highest resolution

docusaurus/docs/Android/05-ui-cookbook/15-watching-livestream.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description: How to watch a livestream on android with Kotlin
66
This cookbook tutorial walks you through how to build an advanced UIs for watching a livestream on Android.
77

88
:::note
9-
n this cookbook tutorial, we will assume that you already know how to join a livestream call. If you haven't familiarized yourself with the [Livestream Tutorial](../02-tutorials/03-livestream.mdx) yet, we highly recommend doing so before proceeding with this cookbook.
9+
In this cookbook tutorial, we will assume that you already know how to join a livestream call. If you haven't familiarized yourself with the [Livestream Tutorial](../02-tutorials/03-livestream.mdx) yet, we highly recommend doing so before proceeding with this cookbook.
1010
:::
1111

1212
When you build a livestreaming UI, there are a few things to keep in mind:

dogfooding/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ dependencies {
241241
implementation(libs.androidx.compose.material.iconsExtended)
242242
implementation(libs.androidx.hilt.navigation)
243243
implementation(libs.landscapist.coil)
244+
implementation(libs.accompanist.permission)
244245

245246
// hilt
246247
implementation(libs.hilt.android)
@@ -252,6 +253,9 @@ dependencies {
252253
// Play Install Referrer library - used to extract the meeting link from demo flow after install
253254
implementation(libs.play.install.referrer)
254255

256+
// Only used for launching a QR code scanner in demo app
257+
implementation(libs.play.code.scanner)
258+
255259
// memory detection
256260
debugImplementation(libs.leakCanary)
257261

dogfooding/src/main/kotlin/io/getstream/video/android/ui/join/CallJoinScreen.kt

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
package io.getstream.video.android.ui.join
2020

21+
import android.net.Uri
22+
import android.widget.Toast
2123
import androidx.compose.foundation.BorderStroke
2224
import androidx.compose.foundation.Image
2325
import androidx.compose.foundation.background
@@ -36,9 +38,12 @@ import androidx.compose.foundation.layout.width
3638
import androidx.compose.foundation.layout.widthIn
3739
import androidx.compose.foundation.rememberScrollState
3840
import androidx.compose.foundation.shape.RoundedCornerShape
41+
import androidx.compose.foundation.text.KeyboardActions
3942
import androidx.compose.foundation.text.KeyboardOptions
4043
import androidx.compose.foundation.verticalScroll
4144
import androidx.compose.material.ButtonDefaults
45+
import androidx.compose.material.Icon
46+
import androidx.compose.material.IconButton
4247
import androidx.compose.material.Text
4348
import androidx.compose.material.TextButton
4449
import androidx.compose.material.TextField
@@ -67,7 +72,12 @@ import androidx.compose.ui.tooling.preview.Preview
6772
import androidx.compose.ui.unit.dp
6873
import androidx.compose.ui.unit.sp
6974
import androidx.hilt.navigation.compose.hiltViewModel
75+
import com.google.android.gms.tasks.OnSuccessListener
76+
import com.google.mlkit.vision.barcode.common.Barcode
77+
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
78+
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
7079
import io.getstream.video.android.BuildConfig
80+
import io.getstream.video.android.DeeplinkingActivity
7181
import io.getstream.video.android.R
7282
import io.getstream.video.android.compose.theme.VideoTheme
7383
import io.getstream.video.android.compose.ui.components.avatar.UserAvatar
@@ -86,6 +96,8 @@ fun CallJoinScreen(
8696
) {
8797
val uiState by callJoinViewModel.uiState.collectAsState(CallJoinUiState.Nothing)
8898
val isLoggedOut by callJoinViewModel.isLoggedOut.collectAsState(initial = false)
99+
val qrCodeCallback = rememberQrCodeCallback()
100+
val context = LocalContext.current
89101

90102
HandleCallJoinUiState(
91103
callJoinUiState = uiState,
@@ -111,6 +123,12 @@ fun CallJoinScreen(
111123
.verticalScroll(rememberScrollState())
112124
.weight(1f),
113125
callJoinViewModel = callJoinViewModel,
126+
openCamera = {
127+
val options = GmsBarcodeScannerOptions.Builder()
128+
.setBarcodeFormats(Barcode.FORMAT_QR_CODE, Barcode.FORMAT_AZTEC).build()
129+
val scanner = GmsBarcodeScanning.getClient(context, options)
130+
scanner.startScan().addOnSuccessListener(qrCodeCallback)
131+
},
114132
)
115133
}
116134

@@ -174,6 +192,7 @@ private fun CallJoinHeader(
174192
@Composable
175193
private fun CallJoinBody(
176194
modifier: Modifier,
195+
openCamera: () -> Unit,
177196
callJoinViewModel: CallJoinViewModel = hiltViewModel(),
178197
) {
179198
val user by if (LocalInspectionMode.current) {
@@ -217,7 +236,7 @@ private fun CallJoinBody(
217236
color = Colors.description,
218237
textAlign = TextAlign.Center,
219238
fontSize = 18.sp,
220-
modifier = Modifier.widthIn(0.dp, 350.dp),
239+
modifier = Modifier.widthIn(0.dp, 320.dp),
221240
)
222241

223242
Spacer(modifier = Modifier.height(42.dp))
@@ -258,7 +277,24 @@ private fun CallJoinBody(
258277
),
259278
shape = RoundedCornerShape(6.dp),
260279
value = callId,
280+
singleLine = true,
261281
onValueChange = { callId = it },
282+
trailingIcon = {
283+
IconButton(
284+
onClick = openCamera,
285+
modifier = Modifier.fillMaxHeight(),
286+
content = {
287+
Icon(
288+
painter = painterResource(id = R.drawable.ic_scan_qr),
289+
contentDescription = stringResource(
290+
id = R.string.join_call_by_qr_code,
291+
),
292+
tint = Colors.description,
293+
modifier = Modifier.size(36.dp),
294+
)
295+
},
296+
)
297+
},
262298
colors = TextFieldDefaults.textFieldColors(
263299
textColor = Color.White,
264300
focusedLabelColor = VideoTheme.colors.primaryAccent,
@@ -275,6 +311,11 @@ private fun CallJoinBody(
275311
color = Color(0xFF5D6168),
276312
)
277313
},
314+
keyboardActions = KeyboardActions(
315+
onDone = {
316+
callJoinViewModel.handleUiEvent(CallJoinEvent.JoinCall(callId = callId))
317+
},
318+
),
278319
)
279320

280321
StreamButton(
@@ -333,6 +374,37 @@ private fun HandleCallJoinUiState(
333374
}
334375
}
335376

377+
@Composable
378+
private fun rememberQrCodeCallback(): OnSuccessListener<Barcode> {
379+
val context = LocalContext.current
380+
381+
return remember {
382+
OnSuccessListener<Barcode> {
383+
val url = it.url?.url
384+
val callId = if (url != null) {
385+
val id = Uri.parse(url).getQueryParameter("id")
386+
if (!id.isNullOrEmpty()) {
387+
id
388+
} else {
389+
null
390+
}
391+
} else {
392+
null
393+
}
394+
395+
if (!callId.isNullOrEmpty()) {
396+
context.startActivity(DeeplinkingActivity.createIntent(context, callId))
397+
} else {
398+
Toast.makeText(
399+
context,
400+
"Unrecognised meeting QR code format",
401+
Toast.LENGTH_SHORT,
402+
).show()
403+
}
404+
}
405+
}
406+
}
407+
336408
@Preview
337409
@Composable
338410
private fun CallJoinScreenPreview() {

dogfooding/src/main/kotlin/io/getstream/video/android/ui/lobby/CallLobbyScreen.kt

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -231,26 +231,20 @@ private fun CallLobbyBody(
231231
isMicrophoneEnabled = isMicrophoneEnabled,
232232
onCallAction = { action ->
233233
when (action) {
234-
is ToggleCamera -> callLobbyViewModel.enableCamera(!isCameraEnabled)
235-
is ToggleMicrophone -> callLobbyViewModel.enableMicrophone(!isMicrophoneEnabled)
234+
is ToggleCamera -> callLobbyViewModel.enableCamera(action.isEnabled)
235+
is ToggleMicrophone -> callLobbyViewModel.enableMicrophone(action.isEnabled)
236236
else -> Unit
237237
}
238238
},
239239
)
240240

241-
LobbyDescription(
242-
callLobbyViewModel = callLobbyViewModel,
243-
cameraEnabled = isCameraEnabled,
244-
microphoneEnabled = isMicrophoneEnabled,
245-
)
241+
LobbyDescription(callLobbyViewModel = callLobbyViewModel)
246242
}
247243
}
248244

249245
@Composable
250246
private fun LobbyDescription(
251247
callLobbyViewModel: CallLobbyViewModel,
252-
cameraEnabled: Boolean,
253-
microphoneEnabled: Boolean,
254248
) {
255249
val session by callLobbyViewModel.call.state.session.collectAsState()
256250

@@ -280,10 +274,7 @@ private fun LobbyDescription(
280274
text = stringResource(id = R.string.join_call),
281275
onClick = {
282276
callLobbyViewModel.handleUiEvent(
283-
CallLobbyEvent.JoinCall(
284-
cameraEnabled = cameraEnabled,
285-
microphoneEnabled = microphoneEnabled,
286-
),
277+
CallLobbyEvent.JoinCall,
287278
)
288279
},
289280
)

dogfooding/src/main/kotlin/io/getstream/video/android/ui/lobby/CallLobbyViewModel.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@ class CallLobbyViewModel @Inject constructor(
144144
is CallLobbyEvent.JoinCall -> {
145145
flowOf(CallLobbyUiState.JoinCompleted)
146146
}
147+
147148
is CallLobbyEvent.JoinFailed -> {
148149
flowOf(CallLobbyUiState.JoinFailed(event.reason))
149150
}
150-
else -> flowOf(CallLobbyUiState.Nothing)
151151
}
152152
}
153153
.onCompletion { _isLoading.value = false }
@@ -178,17 +178,16 @@ class CallLobbyViewModel @Inject constructor(
178178
}
179179

180180
sealed interface CallLobbyUiState {
181-
object Nothing : CallLobbyUiState
181+
data object Nothing : CallLobbyUiState
182182

183-
object JoinCompleted : CallLobbyUiState
183+
data object JoinCompleted : CallLobbyUiState
184184

185185
data class JoinFailed(val reason: String?) : CallLobbyUiState
186186
}
187187

188188
sealed interface CallLobbyEvent {
189-
object Nothing : CallLobbyEvent
190189

191-
class JoinCall(val cameraEnabled: Boolean, val microphoneEnabled: Boolean) : CallLobbyEvent
190+
data object JoinCall : CallLobbyEvent
192191

193192
data class JoinFailed(val reason: String?) : CallLobbyEvent
194193
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
Copyright (c) 2023 Stream.io Inc. All rights reserved.
4+
5+
Licensed under the Stream License;
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
https://github.com/GetStream/stream-video-android/blob/main/LICENSE
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
android:width="24dp"
19+
android:height="24dp"
20+
android:viewportWidth="24"
21+
android:viewportHeight="24">
22+
<path
23+
android:fillColor="#FF000000"
24+
android:pathData="M16.167,6C16.075,6 16,6.075 16,6.167L16,7.833C16,7.925 16.075,8 16.167,8L17.833,8C17.925,8 18,7.925 18,7.833L18,6.167C18,6.075 17.925,6 17.833,6L16.167,6ZM16,18L16,17.5C16,17.224 16.224,17 16.5,17C16.776,17 17,17.224 17,17.5L17,18L18,18L18,17.5C18,17.224 18.224,17 18.5,17C18.776,17 19,17.224 19,17.5L19,18.5C19,18.776 18.776,19 18.5,19L14.5,19C14.224,19 14,18.776 14,18.5L14,17.5C14,17.224 14.224,17 14.5,17C14.776,17 15,17.224 15,17.5L15,18L16,18L16,18ZM13,11L13.5,11C13.776,11 14,11.224 14,11.5C14,11.776 13.776,12 13.5,12L11.5,12C11.224,12 11,11.776 11,11.5C11,11.224 11.224,11 11.5,11L12,11L12,10L10.5,10C10.224,10 10,9.776 10,9.5C10,9.224 10.224,9 10.5,9L13.5,9C13.776,9 14,9.224 14,9.5C14,9.776 13.776,10 13.5,10L13,10L13,11ZM18,12L17.5,12C17.224,12 17,11.776 17,11.5C17,11.224 17.224,11 17.5,11L18,11L18,10.5C18,10.224 18.224,10 18.5,10C18.776,10 19,10.224 19,10.5L19,12.5C19,12.776 18.776,13 18.5,13C18.224,13 18,12.776 18,12.5L18,12ZM13,14L12.5,14C12.224,14 12,13.776 12,13.5C12,13.224 12.224,13 12.5,13L13.5,13C13.776,13 14,13.224 14,13.5L14,15.5C14,15.776 13.776,16 13.5,16L10.5,16C10.224,16 10,15.776 10,15.5C10,15.224 10.224,15 10.5,15L13,15L13,14L13,14ZM16.167,5L17.833,5C18.478,5 19,5.522 19,6.167L19,7.833C19,8.478 18.478,9 17.833,9L16.167,9C15.522,9 15,8.478 15,7.833L15,6.167C15,5.522 15.522,5 16.167,5ZM6.167,5L7.833,5C8.478,5 9,5.522 9,6.167L9,7.833C9,8.478 8.478,9 7.833,9L6.167,9C5.522,9 5,8.478 5,7.833L5,6.167C5,5.522 5.522,5 6.167,5ZM6.167,6C6.075,6 6,6.075 6,6.167L6,7.833C6,7.925 6.075,8 6.167,8L7.833,8C7.925,8 8,7.925 8,7.833L8,6.167C8,6.075 7.925,6 7.833,6L6.167,6ZM6.167,15L7.833,15C8.478,15 9,15.522 9,16.167L9,17.833C9,18.478 8.478,19 7.833,19L6.167,19C5.522,19 5,18.478 5,17.833L5,16.167C5,15.522 5.522,15 6.167,15ZM6.167,16C6.075,16 6,16.075 6,16.167L6,17.833C6,17.925 6.075,18 6.167,18L7.833,18C7.925,18 8,17.925 8,17.833L8,16.167C8,16.075 7.925,16 7.833,16L6.167,16ZM13,6L10.5,6C10.224,6 10,5.776 10,5.5C10,5.224 10.224,5 10.5,5L13.5,5C13.776,5 14,5.224 14,5.5L14,7.5C14,7.776 13.776,8 13.5,8C13.224,8 13,7.776 13,7.5L13,6ZM10.5,8C10.224,8 10,7.776 10,7.5C10,7.224 10.224,7 10.5,7L11.5,7C11.776,7 12,7.224 12,7.5C12,7.776 11.776,8 11.5,8L10.5,8ZM5.5,14C5.224,14 5,13.776 5,13.5C5,13.224 5.224,13 5.5,13L7.5,13C7.776,13 8,13.224 8,13.5C8,13.776 7.776,14 7.5,14L5.5,14ZM9.5,14C9.224,14 9,13.776 9,13.5C9,13.224 9.224,13 9.5,13L10.5,13C10.776,13 11,13.224 11,13.5C11,13.776 10.776,14 10.5,14L9.5,14ZM11,18L11,18.5C11,18.776 10.776,19 10.5,19C10.224,19 10,18.776 10,18.5L10,17.5C10,17.224 10.224,17 10.5,17L12.5,17C12.776,17 13,17.224 13,17.5C13,17.776 12.776,18 12.5,18L11,18ZM9,11L9.5,11C9.776,11 10,11.224 10,11.5C10,11.776 9.776,12 9.5,12L8.5,12C8.224,12 8,11.776 8,11.5L8,11L7.5,11C7.224,11 7,10.776 7,10.5C7,10.224 7.224,10 7.5,10L8.5,10C8.776,10 9,10.224 9,10.5L9,11ZM5,10.5C5,10.224 5.224,10 5.5,10C5.776,10 6,10.224 6,10.5L6,11.5C6,11.776 5.776,12 5.5,12C5.224,12 5,11.776 5,11.5L5,10.5ZM15,10.5C15,10.224 15.224,10 15.5,10C15.776,10 16,10.224 16,10.5L16,12.5C16,12.776 15.776,13 15.5,13C15.224,13 15,12.776 15,12.5L15,10.5ZM17,15L17,14.5C17,14.224 17.224,14 17.5,14L18.5,14C18.776,14 19,14.224 19,14.5C19,14.776 18.776,15 18.5,15L18,15L18,15.5C18,15.776 17.776,16 17.5,16L15.5,16C15.224,16 15,15.776 15,15.5L15,14.5C15,14.224 15.224,14 15.5,14C15.776,14 16,14.224 16,14.5L16,15L17,15ZM3,6.5C3,6.776 2.776,7 2.5,7C2.224,7 2,6.776 2,6.5L2,4.5C2,3.119 3.119,2 4.5,2L6.5,2C6.776,2 7,2.224 7,2.5C7,2.776 6.776,3 6.5,3L4.5,3C3.672,3 3,3.672 3,4.5L3,6.5ZM17.5,3C17.224,3 17,2.776 17,2.5C17,2.224 17.224,2 17.5,2L19.5,2C20.881,2 22,3.119 22,4.5L22,6.5C22,6.776 21.776,7 21.5,7C21.224,7 21,6.776 21,6.5L21,4.5C21,3.672 20.328,3 19.5,3L17.5,3ZM6.5,21C6.776,21 7,21.224 7,21.5C7,21.776 6.776,22 6.5,22L4.5,22C3.119,22 2,20.881 2,19.5L2,17.5C2,17.224 2.224,17 2.5,17C2.776,17 3,17.224 3,17.5L3,19.5C3,20.328 3.672,21 4.5,21L6.5,21ZM21,17.5C21,17.224 21.224,17 21.5,17C21.776,17 22,17.224 22,17.5L22,19.5C22,20.881 20.881,22 19.5,22L17.5,22C17.224,22 17,21.776 17,21.5C17,21.224 17.224,21 17.5,21L19.5,21C20.328,21 21,20.328 21,19.5L21,17.5Z"/>
25+
</vector>

dogfooding/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<string name="sign_out">Sign out</string>
2727
<string name="join_description">Build reliable video calling, audio rooms, and live streaming with our easy-to-use SDKs and global edge network</string>
2828
<string name="join_call">Join Call</string>
29+
<string name="join_call_by_qr_code">Scan QR meeting code</string>
2930
<string name="join_call_description">You are about to join a call. %d more people are in the call.</string>
3031
<string name="join_call_no_id_hint">Don\'t have a Call ID?</string>
3132
<string name="join_call_call_id_hint">Call ID</string>

gradle/libs.versions.toml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[versions]
2-
androidGradlePlugin = "8.1.0"
3-
spotless = "6.20.0"
2+
androidGradlePlugin = "8.1.1"
3+
spotless = "6.21.0"
44
nexusPlugin = "1.3.0"
5-
kotlin = "1.9.0"
6-
kotlinSerialization = "1.5.1"
5+
kotlin = "1.9.10"
6+
kotlinSerialization = "1.6.0"
77
kotlinSerializationConverter = "1.0.0"
88
kotlinxCoroutines = "1.7.3"
99

@@ -21,14 +21,14 @@ androidxDataStore = "1.0.0"
2121
googleService = "4.3.14"
2222

2323
androidxComposeBom = "2023.08.00"
24-
androidxComposeCompiler = "1.5.2"
24+
androidxComposeCompiler = "1.5.3"
2525
androidxComposeTracing = "1.0.0-alpha03"
2626
androidxHiltNavigation = "1.0.0"
2727
androidxComposeNavigation = "2.7.0"
2828
composeStableMarker = "1.0.0"
2929

30-
coil = "2.2.2"
31-
landscapist = "2.2.7"
30+
coil = "2.4.0"
31+
landscapist = "2.2.8"
3232
accompanist = "0.30.1"
3333
telephoto = "0.3.0"
3434
audioswitch = "1.1.8"
@@ -50,7 +50,7 @@ streamPush = "1.1.1"
5050
androidxTest = "1.5.2"
5151
androidxTestCore = "1.5.0"
5252
androidxProfileinstaller = "1.3.1"
53-
androidxMacroBenchmark = "1.2.0-beta03"
53+
androidxMacroBenchmark = "1.2.0-beta05"
5454
androidxUiAutomator = "2.3.0-alpha04"
5555
androidxContraintLayout = "2.1.4"
5656
androidxEspresso = "3.5.1"
@@ -65,6 +65,7 @@ firebaseBom = "32.1.0"
6565
firebaseCrashlytics = "2.9.5"
6666

6767
installReferrer = "2.2"
68+
playCodeScanner = "16.1.0"
6869

6970
hilt = "2.46.1"
7071
desugar = "2.0.3"
@@ -178,6 +179,7 @@ firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashly
178179
firebase-analyrics = { group = "com.google.firebase", name = "firebase-analytics-ktx" }
179180

180181
play-install-referrer = { group = "com.android.installreferrer", name = "installreferrer", version.ref = "installReferrer" }
182+
play-code-scanner = { group = "com.google.android.gms", name = "play-services-code-scanner", version.ref = "playCodeScanner" }
181183

182184
robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" }
183185
leakCanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakCanary" }

gradle/wrapper/gradle-wrapper.jar

1.73 KB
Binary file not shown.

0 commit comments

Comments
 (0)