[Feature] Receive error event when device manager throws any error (#385)
This commit is contained in:
Родитель
fdb97aa35f
Коммит
a36c79567e
|
@ -1,3 +1,6 @@
|
|||
# Release History
|
||||
|
||||
[Communication UI Calling Library CHANGELOG](docs/CHANGELOG_UI_CALLING.md)
|
||||
|
||||
### Features
|
||||
- New error code 'unknownError' introduced for ambiguous errors such as device manager error
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
package com.azure.android.communication.mocking
|
||||
|
||||
import androidx.annotation.GuardedBy
|
||||
import com.azure.android.communication.calling.CallState
|
||||
import com.azure.android.communication.calling.CameraFacing
|
||||
import com.azure.android.communication.calling.MediaStreamType
|
||||
import com.azure.android.communication.calling.ParticipantState
|
||||
import com.azure.android.communication.calling.PropertyChangedListener
|
||||
import com.azure.android.communication.calling.RemoteVideoStreamsUpdatedListener
|
||||
import com.azure.android.communication.calling.VideoDeviceType
|
||||
import com.azure.android.communication.calling.ParticipantState
|
||||
import com.azure.android.communication.calling.MediaStreamType
|
||||
import com.azure.android.communication.calling.CallState
|
||||
import com.azure.android.communication.calling.RemoteVideoStreamsUpdatedListener
|
||||
import com.azure.android.communication.calling.PropertyChangedListener
|
||||
import com.azure.android.communication.ui.calling.models.ParticipantInfoModel
|
||||
import com.azure.android.communication.ui.calling.models.StreamType
|
||||
import com.azure.android.communication.ui.calling.models.VideoStreamModel
|
||||
|
@ -137,7 +137,9 @@ internal class TestCallingSDK(private val callEvents: CallEvents, coroutineConte
|
|||
emitRemoteParticipantFlow()
|
||||
}
|
||||
|
||||
override fun setupCall() {}
|
||||
override fun setupCall(): CompletableFuture<Void> {
|
||||
return completedNullFuture()
|
||||
}
|
||||
override fun dispose() {}
|
||||
|
||||
override fun turnOnVideoAsync(): CompletableFuture<LocalVideoStream> {
|
||||
|
|
|
@ -14,6 +14,7 @@ internal class ErrorCode : ExpandableStringEnum<ErrorCode?>() {
|
|||
val TURN_CAMERA_OFF_FAILED = fromString("turnCameraOffFailed")
|
||||
val TURN_MIC_ON_FAILED = fromString("turnMicOnFailed")
|
||||
val TURN_MIC_OFF_FAILED = fromString("turnMicOffFailed")
|
||||
val UNKNOWN_ERROR = fromString("unknownError")
|
||||
|
||||
private fun fromString(name: String): ErrorCode {
|
||||
return fromString(name, ErrorCode::class.java)
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.azure.android.communication.ui.calling.error.ErrorCode.Companion.SWIT
|
|||
import com.azure.android.communication.ui.calling.error.ErrorCode.Companion.TOKEN_EXPIRED
|
||||
import com.azure.android.communication.ui.calling.error.ErrorCode.Companion.TURN_CAMERA_OFF_FAILED
|
||||
import com.azure.android.communication.ui.calling.error.ErrorCode.Companion.TURN_CAMERA_ON_FAILED
|
||||
import com.azure.android.communication.ui.calling.error.ErrorCode.Companion.UNKNOWN_ERROR
|
||||
import com.azure.android.communication.ui.calling.models.CallCompositeErrorCode
|
||||
import com.azure.android.communication.ui.calling.models.CallCompositeErrorEvent
|
||||
import com.azure.android.communication.ui.calling.models.CallCompositeEventCode
|
||||
|
@ -118,6 +119,9 @@ internal class ErrorHandler(
|
|||
TOKEN_EXPIRED -> {
|
||||
return CallCompositeErrorCode.TOKEN_EXPIRED
|
||||
}
|
||||
UNKNOWN_ERROR -> {
|
||||
return CallCompositeErrorCode.UNKNOWN_ERROR
|
||||
}
|
||||
CALL_JOIN_FAILED -> {
|
||||
return CallCompositeErrorCode.CALL_JOIN_FAILED
|
||||
}
|
||||
|
|
|
@ -27,12 +27,16 @@ public final class CallCompositeErrorCode extends ExpandableStringEnum<CallCompo
|
|||
*/
|
||||
public static final CallCompositeErrorCode TOKEN_EXPIRED = fromString("tokenExpired");
|
||||
|
||||
|
||||
/**
|
||||
* Dispatched when camera failed to start, stop or switch
|
||||
*/
|
||||
public static final CallCompositeErrorCode CAMERA_FAILURE = fromString("cameraFailure");
|
||||
|
||||
/***
|
||||
* Dispatched when composite falls under any ambiguous state such as device manager instance error
|
||||
*/
|
||||
public static final CallCompositeErrorCode UNKNOWN_ERROR = fromString("unknownError");
|
||||
|
||||
/**
|
||||
* Creates or finds a {@link CallCompositeErrorCode} from its string representation.
|
||||
*
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.azure.android.communication.ui.calling.redux.state.AudioOperationalSt
|
|||
import com.azure.android.communication.ui.calling.redux.state.CameraDeviceSelectionStatus
|
||||
|
||||
internal sealed class LocalParticipantAction : Action {
|
||||
class DeviceManagerFetchFailed(val error: CallCompositeError) : LocalParticipantAction()
|
||||
class CameraPreviewOnRequested : LocalParticipantAction()
|
||||
class CameraPreviewOnTriggered : LocalParticipantAction()
|
||||
class CameraPreviewOnSucceeded(var videoStreamID: String) : LocalParticipantAction()
|
||||
|
|
|
@ -199,7 +199,15 @@ internal class CallingMiddlewareActionHandlerImpl(
|
|||
}
|
||||
|
||||
override fun setupCall(store: Store<ReduxState>) {
|
||||
callingService.setupCall()
|
||||
callingService.setupCall().handle { _, error: Throwable? ->
|
||||
if (error != null) {
|
||||
store.dispatch(
|
||||
ErrorAction.FatalErrorOccurred(
|
||||
FatalError(error, ErrorCode.UNKNOWN_ERROR)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun startCall(store: Store<ReduxState>) {
|
||||
|
|
|
@ -102,8 +102,8 @@ internal class CallingService(
|
|||
return callingSdk.resume()
|
||||
}
|
||||
|
||||
fun setupCall() {
|
||||
callingSdk.setupCall()
|
||||
fun setupCall(): CompletableFuture<Void> {
|
||||
return callingSdk.setupCall()
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
package com.azure.android.communication.ui.calling.service.sdk
|
||||
|
||||
import android.view.View
|
||||
import com.azure.android.communication.calling.CameraFacing
|
||||
import com.azure.android.communication.calling.CreateViewOptions
|
||||
import com.azure.android.communication.calling.MediaStreamType
|
||||
import com.azure.android.communication.calling.ParticipantState
|
||||
import com.azure.android.communication.calling.PropertyChangedListener
|
||||
import com.azure.android.communication.calling.RemoteVideoStreamsUpdatedListener
|
||||
import com.azure.android.communication.calling.MediaStreamType
|
||||
import com.azure.android.communication.calling.CameraFacing
|
||||
import com.azure.android.communication.calling.VideoDeviceType
|
||||
import com.azure.android.communication.calling.CreateViewOptions
|
||||
import com.azure.android.communication.ui.calling.models.ParticipantInfoModel
|
||||
import com.azure.android.communication.ui.calling.redux.state.AudioState
|
||||
import com.azure.android.communication.ui.calling.redux.state.CameraDeviceSelectionStatus
|
||||
|
@ -24,7 +24,7 @@ import kotlinx.coroutines.flow.SharedFlow
|
|||
*/
|
||||
internal interface CallingSDK {
|
||||
// Internal helpers. Refactor these out further.
|
||||
fun setupCall()
|
||||
fun setupCall(): CompletableFuture<Void>
|
||||
fun dispose()
|
||||
|
||||
// Interactions.
|
||||
|
|
|
@ -136,20 +136,29 @@ internal class CallingSDKWrapper(
|
|||
cleanupResources()
|
||||
}
|
||||
|
||||
override fun setupCall() {
|
||||
override fun setupCall(): CompletableFuture<Void> {
|
||||
if (callClient == null) {
|
||||
val callClientOptions = CallClientOptions().also {
|
||||
it.setTags(configuration.callConfig?.diagnosticConfig?.tags, logger)
|
||||
}
|
||||
callClient = CallClient(callClientOptions)
|
||||
}
|
||||
createDeviceManager()
|
||||
val setupCallCompletableFuture: CompletableFuture<Void> = CompletableFuture()
|
||||
createDeviceManager().handle { _, error: Throwable? ->
|
||||
if (error != null) {
|
||||
setupCallCompletableFuture.completeExceptionally(error)
|
||||
} else {
|
||||
setupCallCompletableFuture.complete(null)
|
||||
}
|
||||
}
|
||||
return setupCallCompletableFuture
|
||||
}
|
||||
|
||||
override fun startCall(
|
||||
cameraState: CameraState,
|
||||
audioState: AudioState,
|
||||
): CompletableFuture<Void> {
|
||||
|
||||
val startCallCompletableFuture = CompletableFuture<Void>()
|
||||
createCallAgent().thenAccept { agent: CallAgent ->
|
||||
val audioOptions = AudioOptions()
|
||||
|
@ -357,7 +366,7 @@ internal class CallingSDKWrapper(
|
|||
return deviceManagerCompletableFuture!!
|
||||
}
|
||||
|
||||
private fun createDeviceManager() {
|
||||
private fun createDeviceManager(): CompletableFuture<DeviceManager> {
|
||||
val deviceManagerCompletableFuture = getDeviceManagerCompletableFuture()
|
||||
|
||||
if (deviceManagerCompletableFuture.isCompletedExceptionally ||
|
||||
|
@ -378,18 +387,20 @@ internal class CallingSDKWrapper(
|
|||
CompletableFuture.allOf(
|
||||
deviceManagerCompletableFuture,
|
||||
)
|
||||
return deviceManagerCompletableFuture
|
||||
}
|
||||
|
||||
private fun initializeCameras(): CompletableFuture<Void> {
|
||||
if (camerasInitializedCompletableFuture == null) {
|
||||
camerasInitializedCompletableFuture = CompletableFuture<Void>()
|
||||
getDeviceManagerCompletableFuture().whenComplete { deviceManager: DeviceManager, _: Throwable? ->
|
||||
getDeviceManagerCompletableFuture().whenComplete { deviceManager: DeviceManager?, error: Throwable? ->
|
||||
|
||||
completeCamerasInitializedCompletableFuture()
|
||||
videoDevicesUpdatedListener =
|
||||
VideoDevicesUpdatedListener {
|
||||
completeCamerasInitializedCompletableFuture()
|
||||
}
|
||||
deviceManager.addOnCamerasUpdatedListener(videoDevicesUpdatedListener)
|
||||
deviceManager?.addOnCamerasUpdatedListener(videoDevicesUpdatedListener)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.azure.android.communication.ui.calling.redux.state.ReduxState
|
|||
import com.azure.android.communication.ui.calling.service.CallingService
|
||||
import com.azure.android.communication.ui.ACSBaseTestCoroutine
|
||||
import com.azure.android.communication.ui.calling.error.ErrorCode.Companion.CALL_END_FAILED
|
||||
import com.azure.android.communication.ui.calling.error.ErrorCode.Companion.UNKNOWN_ERROR
|
||||
import com.azure.android.communication.ui.calling.models.CallCompositeEventCode.Companion.CALL_DECLINED
|
||||
import com.azure.android.communication.ui.calling.models.CallCompositeEventCode.Companion.CALL_EVICTED
|
||||
import com.azure.android.communication.ui.helper.UnconfinedTestContextProvider
|
||||
|
@ -475,6 +476,40 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
|
|||
)
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@Test
|
||||
fun callingMiddlewareActionHandler_setupCall_fails_then_dispatchFatalError() =
|
||||
runScopedTest {
|
||||
val appState = AppReduxState("")
|
||||
appState.callState = CallingState(CallingStatus.NONE)
|
||||
|
||||
val setupCallCompletableFuture: CompletableFuture<Void> = CompletableFuture()
|
||||
val mockCallingService: CallingService = mock {
|
||||
on { setupCall() } doReturn setupCallCompletableFuture
|
||||
}
|
||||
|
||||
val handler = CallingMiddlewareActionHandlerImpl(
|
||||
mockCallingService,
|
||||
UnconfinedTestContextProvider()
|
||||
)
|
||||
|
||||
val mockAppStore = mock<AppStore<ReduxState>> {
|
||||
on { dispatch(any()) } doAnswer { }
|
||||
}
|
||||
|
||||
val exception = Exception("test")
|
||||
handler.setupCall(mockAppStore)
|
||||
setupCallCompletableFuture.completeExceptionally(exception)
|
||||
|
||||
// assert
|
||||
verify(mockAppStore, times(1)).dispatch(
|
||||
argThat { action ->
|
||||
action is ErrorAction.FatalErrorOccurred &&
|
||||
action.error.fatalError == exception && action.error.errorCode == ErrorCode.UNKNOWN_ERROR
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@Test
|
||||
fun callingMiddlewareActionHandler_startCall_fails_then_dispatchFatalError() =
|
||||
|
|
Загрузка…
Ссылка в новой задаче