11package com.google.firebase.quickstart.ai.feature.live
22
33import android.Manifest
4+ import android.content.Context
5+ import android.content.pm.PackageManager
6+ import androidx.activity.compose.rememberLauncherForActivityResult
7+ import androidx.activity.result.contract.ActivityResultContracts
48import androidx.annotation.RequiresPermission
59import androidx.compose.foundation.layout.Box
10+ import androidx.compose.foundation.layout.Column
611import androidx.compose.foundation.layout.fillMaxHeight
712import androidx.compose.foundation.layout.fillMaxSize
813import androidx.compose.material3.MaterialTheme
914import androidx.compose.material3.Surface
15+ import androidx.compose.material3.Text
1016import androidx.compose.runtime.Composable
1117import androidx.compose.runtime.DisposableEffect
18+ import androidx.compose.runtime.LaunchedEffect
19+ import androidx.compose.runtime.getValue
20+ import androidx.compose.runtime.mutableStateOf
21+ import androidx.compose.runtime.remember
1222import androidx.compose.runtime.rememberCoroutineScope
23+ import androidx.compose.runtime.setValue
1324import androidx.compose.ui.Modifier
25+ import androidx.compose.ui.platform.LocalContext
26+ import androidx.core.content.ContextCompat
1427import androidx.lifecycle.viewmodel.compose.viewModel
15- import com.google.firebase.quickstart.ai.feature.media.imagen .BidiViewModel
28+ import com.google.firebase.quickstart.ai.feature.live .BidiViewModel
1629import kotlinx.coroutines.launch
1730import kotlinx.serialization.Serializable
1831
@@ -26,9 +39,42 @@ fun StreamRealtimeVideoScreen(bidiView: BidiViewModel = viewModel<BidiViewModel>
2639 MaterialTheme .colorScheme.background
2740
2841 val scope = rememberCoroutineScope()
29- DisposableEffect (Unit ) {
30- scope.launch {
31- bidiView.startConversation()
42+
43+ val context = LocalContext .current
44+ var hasPermissions by remember {
45+ mutableStateOf(
46+ ContextCompat .checkSelfPermission(
47+ context,
48+ Manifest .permission.CAMERA
49+ ) == PackageManager .PERMISSION_GRANTED && ContextCompat .checkSelfPermission(
50+ context,
51+ Manifest .permission.RECORD_AUDIO
52+ ) == PackageManager .PERMISSION_GRANTED
53+ )
54+ }
55+
56+ val launcher = rememberLauncherForActivityResult(
57+ ActivityResultContracts .RequestMultiplePermissions ()
58+ ) { permissions ->
59+ hasPermissions = permissions.values.all { it }
60+ }
61+
62+ LaunchedEffect (Unit ) {
63+ if (! hasPermissions) {
64+ launcher.launch(
65+ arrayOf(
66+ Manifest .permission.CAMERA ,
67+ Manifest .permission.RECORD_AUDIO
68+ )
69+ )
70+ }
71+ }
72+
73+ DisposableEffect (hasPermissions) {
74+ if (hasPermissions) {
75+ scope.launch {
76+ bidiView.startConversation()
77+ }
3278 }
3379 onDispose {
3480 bidiView.endConversation()
@@ -39,16 +85,22 @@ fun StreamRealtimeVideoScreen(bidiView: BidiViewModel = viewModel<BidiViewModel>
3985 modifier = Modifier .fillMaxSize(),
4086 color = backgroundColor
4187 ) {
42- Box (
43- modifier = Modifier
44- .fillMaxSize()
45- ) {
46- CameraView (
47- modifier = Modifier .fillMaxHeight(0.5f ),
48- onFrameCaptured = { bitmap ->
49- bidiView.sendVideoFrame(bitmap)
88+ Column (modifier = Modifier .fillMaxSize()) {
89+ if (hasPermissions) {
90+ Box (
91+ modifier = Modifier
92+ .fillMaxSize()
93+ ) {
94+ CameraView (
95+ modifier = Modifier .fillMaxHeight(0.5f ),
96+ onFrameCaptured = { bitmap ->
97+ bidiView.sendVideoFrame(bitmap)
98+ }
99+ )
50100 }
51- )
101+ } else {
102+ Text (" Camera and audio permissions are required to use this feature." )
103+ }
52104 }
53105 }
54106}
0 commit comments