diff --git a/features/video/build.gradle.kts b/features/video/build.gradle.kts index e9cc320..75c23f4 100644 --- a/features/video/build.gradle.kts +++ b/features/video/build.gradle.kts @@ -39,6 +39,8 @@ dependencies { implementation(project(":core:network")) implementation(project(":core:data")) + implementation(libs.accompanist.permissions) + implementation(libs.stream.video.compose) implementation(libs.stream.video.mock) diff --git a/features/video/src/main/kotlin/io/getstream/whatsappclone/video/WhatsAppVideoCall.kt b/features/video/src/main/kotlin/io/getstream/whatsappclone/video/WhatsAppVideoCall.kt index 213178b..b844648 100644 --- a/features/video/src/main/kotlin/io/getstream/whatsappclone/video/WhatsAppVideoCall.kt +++ b/features/video/src/main/kotlin/io/getstream/whatsappclone/video/WhatsAppVideoCall.kt @@ -16,6 +16,8 @@ package io.getstream.whatsappclone.video +import android.Manifest +import android.os.Build import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size @@ -36,6 +38,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.rememberMultiplePermissionsState import io.getstream.video.android.compose.theme.VideoTheme import io.getstream.video.android.compose.ui.components.call.activecall.CallContent import io.getstream.video.android.compose.ui.components.call.controls.ControlActions @@ -59,7 +63,7 @@ fun WhatsAppVideoCall( ) { val uiState by viewModel.videoUiSate.collectAsStateWithLifecycle() - LaunchedEffect(key1 = id) { + EnsureAudioPermission { viewModel.joinCall(type = "default", id = id.replace(":", "")) } @@ -210,3 +214,30 @@ private fun WhatsAppVideoCallContentPreview() { ) {} } } + +@OptIn(ExperimentalPermissionsApi::class) +@Composable +fun EnsureAudioPermission(onPermissionsGranted: () -> Unit) { + // While the SDK will handle the microphone permission, + // its not a bad idea to do it prior to entering any call UIs + val permissionsState = rememberMultiplePermissionsState( + permissions = buildList { + // Access to microphone + add(Manifest.permission.RECORD_AUDIO) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + // Allow for foreground service for notification on API 26+ + add(Manifest.permission.FOREGROUND_SERVICE) + } + } + ) + + LaunchedEffect(key1 = Unit) { + permissionsState.launchMultiplePermissionRequest() + } + + LaunchedEffect(key1 = permissionsState.allPermissionsGranted) { + if (permissionsState.allPermissionsGranted) { + onPermissionsGranted() + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a0c0d5e..6e6fcc0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -58,6 +58,7 @@ landscapist-animation = { group = "com.github.skydoves", name = "landscapist-ani landscapist-placeholder = { group = "com.github.skydoves", name = "landscapist-placeholder", version.ref = "landscapist" } accompanist-pager = { group = "com.google.accompanist", name = "accompanist-pager", version.ref = "accompanist" } accompanist-indicator = { group = "com.google.accompanist", name = "accompanist-pager-indicators", version.ref = "accompanist" } +accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" }