Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package io.getstream.video.android.compose.permission

import android.content.Context
import android.os.Build
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
Expand All @@ -35,14 +36,15 @@ import io.getstream.video.android.core.notifications.internal.telecom.TelecomPer
*
* - android.Manifest.permission.CAMERA
* - android.Manifest.permission.RECORD_AUDIO
* - android.Manifest.permission.BLUETOOTH_CONNECT on Android 12 and above
*
* You can request those permissions by invoking `launchPermissionRequest()` method.
*/
@OptIn(ExperimentalPermissionsApi::class)
@Composable
public fun rememberCallPermissionsState(
call: Call,
permissions: List<String> = getPermissions(),
permissions: List<String> = getDefaultPermissionList(isVideoCall = true),
onPermissionsResult: ((Map<String, Boolean>) -> Unit)? = null,
onAllPermissionsGranted: (suspend () -> Unit)? = null,
): VideoPermissionsState {
Expand Down Expand Up @@ -86,8 +88,16 @@ public fun rememberCallPermissionsState(
}

@Composable
private fun getPermissions(): List<String> {
val context = LocalContext.current
internal fun getDefaultPermissionList(isVideoCall: Boolean): List<String> =
getDefaultPermissionList(
context = LocalContext.current,
isVideoCall = isVideoCall,
)

internal fun getDefaultPermissionList(
context: Context,
isVideoCall: Boolean,
): List<String> {
val permissionsList = mutableListOf<String>()
val telecomPermissions = TelecomPermissions()

Expand All @@ -100,19 +110,13 @@ private fun getPermissions(): List<String> {
}
}

permissionsList.addAll(
mutableListOf(
android.Manifest.permission.CAMERA,
android.Manifest.permission.RECORD_AUDIO,
),
)
if (isVideoCall) {
permissionsList.add(android.Manifest.permission.CAMERA)
}
permissionsList.add(android.Manifest.permission.RECORD_AUDIO)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
permissionsList.addAll(
mutableListOf(
android.Manifest.permission.BLUETOOTH_CONNECT,
),
)
permissionsList.add(android.Manifest.permission.BLUETOOTH_CONNECT)
}
return permissionsList
}
Expand All @@ -122,6 +126,7 @@ private fun getPermissions(): List<String> {
*
* - android.Manifest.permission.CAMERA
* - android.Manifest.permission.RECORD_AUDIO
* - android.Manifest.permission.BLUETOOTH_CONNECT on Android 12 and above
*/
@Composable
public fun LaunchCallPermissions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.getstream.video.android.compose.permission.VideoPermissionsState
import io.getstream.video.android.compose.permission.getDefaultPermissionList
import io.getstream.video.android.compose.permission.rememberCallPermissionsState
import io.getstream.video.android.compose.theme.AudioOnlyCallControlsContentParams
import io.getstream.video.android.compose.theme.AudioOnlyCallDetailsContentParams
Expand Down Expand Up @@ -84,9 +85,7 @@ public fun AudioCallContent(
isMicrophoneEnabled: Boolean,
permissions: VideoPermissionsState = rememberCallPermissionsState(
call = call,
permissions = listOf(
android.Manifest.permission.RECORD_AUDIO,
),
permissions = getDefaultPermissionList(isVideoCall = false),
),
onCallAction: (CallAction) -> Unit = { action: CallAction ->
DefaultOnCallActionHandler.onCallAction(call, action)
Expand Down Expand Up @@ -165,9 +164,7 @@ public fun AudioOnlyCallContent(
isMicrophoneEnabled: Boolean,
permissions: VideoPermissionsState = rememberCallPermissionsState(
call = call,
permissions = listOf(
android.Manifest.permission.RECORD_AUDIO,
),
permissions = getDefaultPermissionList(isVideoCall = false),
),
isShowingHeader: Boolean = true,
headerContent: (@Composable ColumnScope.() -> Unit)? = null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-video-android/blob/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.getstream.video.android.compose.permission

import android.Manifest
import android.os.Build
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config

@RunWith(RobolectricTestRunner::class)
internal class CallPermissionsTest {
Comment thread
rahul-lohra marked this conversation as resolved.

@Test
@Config(sdk = [Build.VERSION_CODES.S])
fun `audio calls request bluetooth connect permission on Android 12`() {
val permissions = getDefaultPermissionList(
context = RuntimeEnvironment.getApplication(),
isVideoCall = false,
)

assertThat(permissions).containsExactly(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.BLUETOOTH_CONNECT,
).inOrder()
}

@Test
@Config(sdk = [Build.VERSION_CODES.R])
fun `audio calls do not request bluetooth connect permission before Android 12`() {
val permissions = getDefaultPermissionList(
context = RuntimeEnvironment.getApplication(),
isVideoCall = false,
)

assertThat(permissions).containsExactly(Manifest.permission.RECORD_AUDIO)
}

@Test
@Config(sdk = [Build.VERSION_CODES.S])
fun `video calls retain camera and audio permissions`() {
val permissions = getDefaultPermissionList(
context = RuntimeEnvironment.getApplication(),
isVideoCall = true,
)

assertThat(permissions).containsExactly(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.BLUETOOTH_CONNECT,
).inOrder()
}
}
Loading