[Calling][Feature] CSS - public API to retrieve CallId and UI (#507)

This commit is contained in:
pavelprystinka 2022-11-02 15:18:00 -07:00 коммит произвёл GitHub
Родитель db0e0464c7
Коммит 2bb7339e54
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
70 изменённых файлов: 841 добавлений и 267 удалений

Просмотреть файл

@ -67,6 +67,7 @@ internal class TestCallingSDK(private val callEvents: CallEvents, coroutineConte
CallingSDK {
private val coroutineScope = CoroutineScope(coroutineContextProvider.Default)
private var callingStateWrapperSharedFlow = MutableSharedFlow<CallingStateWrapper>()
private var callIdStateFlow = MutableStateFlow<String?>(null)
private var remoteParticipantsInfoModelSharedFlow =
MutableSharedFlow<Map<String, ParticipantInfoModel>>()
private var isMutedSharedFlow = MutableSharedFlow<Boolean>()
@ -194,6 +195,7 @@ internal class TestCallingSDK(private val callEvents: CallEvents, coroutineConte
startCallCompletableFuture.complete(null)
callStarted.compareAndSet(false, true)
callingStateWrapperSharedFlow.emit(CallingStateWrapper(CallState.CONNECTED, 0, 0))
callIdStateFlow.emit("callid")
emitRemoteParticipantFlow()
}
return startCallCompletableFuture
@ -245,6 +247,8 @@ internal class TestCallingSDK(private val callEvents: CallEvents, coroutineConte
return callingStateWrapperSharedFlow
}
override fun getCallIdStateFlow(): StateFlow<String?> = callIdStateFlow
override fun getRemoteParticipantInfoModelSharedFlow(): Flow<Map<String, ParticipantInfoModel>> {
coroutineScope.launch {
emitRemoteParticipantFlow()

Просмотреть файл

@ -10,6 +10,8 @@ import com.azure.android.communication.common.CommunicationIdentifier;
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration;
import com.azure.android.communication.ui.calling.configuration.CallConfiguration;
import com.azure.android.communication.ui.calling.configuration.CallType;
import com.azure.android.communication.ui.calling.di.DependencyInjectionContainer;
import com.azure.android.communication.ui.calling.models.CallCompositeDiagnosticsInfo;
import com.azure.android.communication.ui.calling.models.CallCompositeGroupCallLocator;
import com.azure.android.communication.ui.calling.models.CallCompositeJoinLocator;
import com.azure.android.communication.ui.calling.models.CallCompositeLocalOptions;
@ -20,9 +22,12 @@ import com.azure.android.communication.ui.calling.models.CallCompositeParticipan
import com.azure.android.communication.ui.calling.models.CallCompositeSetParticipantViewDataResult;
import com.azure.android.communication.ui.calling.models.CallCompositeTeamsMeetingLinkLocator;
import com.azure.android.communication.ui.calling.presentation.CallCompositeActivity;
import com.azure.android.communication.ui.calling.presentation.manager.DiagnosticsManager;
import static com.azure.android.communication.ui.calling.models.CallCompositeDiagnosticsInfoExtensionsKt.buildCallCompositeDiagnosticsInfo;
import static com.azure.android.communication.ui.calling.service.sdk.TypeConversionsKt.into;
import java.lang.ref.WeakReference;
import java.util.UUID;
/**
@ -50,6 +55,7 @@ public final class CallComposite {
private static int instanceId = 0;
private final CallCompositeConfiguration configuration;
private WeakReference<DependencyInjectionContainer> diContainer;
CallComposite(final CallCompositeConfiguration configuration) {
this.configuration = configuration;
@ -202,6 +208,26 @@ public final class CallComposite {
.setParticipantViewData(into(identifier), participantViewData);
}
/**
* Get Call Composite Diagnostics information.
*
* @return {@link CallCompositeDiagnosticsInfo}
*/
public CallCompositeDiagnosticsInfo getDiagnosticsInfo() {
final DiagnosticsManager diagnosticsManager = getDiagnosticsManger();
return diagnosticsManager != null
? diagnosticsManager.getDiagnosticsInfo()
: buildCallCompositeDiagnosticsInfo();
}
void setDependencyInjectionContainer(final DependencyInjectionContainer diContainer) {
this.diContainer = new WeakReference<DependencyInjectionContainer>(diContainer);
}
private DiagnosticsManager getDiagnosticsManger() {
return diContainer != null ? diContainer.get().getDiagnosticsManager() : null;
}
private void launchComposite(final Context context,
final CallCompositeRemoteOptions remoteOptions,
final CallCompositeLocalOptions localOptions,
@ -231,7 +257,7 @@ public final class CallComposite {
configuration.setCallCompositeLocalOptions(localOptions);
}
CallCompositeConfiguration.Companion.putConfig(instanceId, configuration);
CallCompositeInstanceManager.putCallComposite(instanceId, this);
final Intent intent = new Intent(context, CallCompositeActivity.class);
intent.putExtra(CallCompositeActivity.KEY_INSTANCE_ID, instanceId++);
@ -241,6 +267,10 @@ public final class CallComposite {
context.startActivity(intent);
}
CallCompositeConfiguration getConfiguration() {
return this.configuration;
}
void launchTest(final Context context,
final CallCompositeRemoteOptions remoteOptions,
final CallCompositeLocalOptions localOptions) {

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.ui.calling
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration
import com.azure.android.communication.ui.calling.di.DependencyInjectionContainer
import java.lang.IllegalStateException
internal class CallCompositeInstanceManager {
/**
* CallCompositeInstance Storage
*
* The configuration for the call requires callbacks, but these callbacks
* can not be passed via intent (not primitive/serializable data).
*
* This is a storage container for CallComposite objects, it uses a weak reference
* to prevent CallComposite from leaking Activities via it's callbacks.
*/
companion object {
private val instances = mutableMapOf<Int, CallComposite>()
/**
* Store a Config by Instance ID
*/
@JvmStatic
fun putCallComposite(id: Int, callComposite: CallComposite) {
instances[id] = callComposite
}
@JvmStatic
fun removeCallComposite(id: Int) {
instances.remove(id)
}
/**
* Gets a config by it's ID
* May return null if the Configuration becomes garbage collected
*/
@JvmStatic
fun getCallComposite(id: Int): CallComposite = instances[id]
?: throw CallCompositeException(
"This ID is not valid, and no entry exists in the map. Please file a bug, this is an error in the composite",
IllegalStateException()
)
/**
* Check if CallComposite exists
*/
@JvmStatic
fun hasCallComposite(id: Int): Boolean = instances.containsKey(id)
}
}
internal fun CallComposite.getConfig(): CallCompositeConfiguration {
return this.configuration
}
internal fun CallComposite.setDependencyInjectionContainer(diContainer: DependencyInjectionContainer) {
return this.setDependencyInjectionContainer(diContainer)
}

Просмотреть файл

@ -3,11 +3,9 @@
package com.azure.android.communication.ui.calling.configuration
import com.azure.android.communication.ui.calling.CallCompositeException
import com.azure.android.communication.ui.calling.configuration.events.CallCompositeEventsHandler
import com.azure.android.communication.ui.calling.models.CallCompositeLocalOptions
import com.azure.android.communication.ui.calling.models.CallCompositeLocalizationOptions
import java.lang.IllegalStateException
internal class CallCompositeConfiguration {
var themeConfig: Int? = null
@ -15,41 +13,5 @@ internal class CallCompositeConfiguration {
var callCompositeEventsHandler = CallCompositeEventsHandler()
var callConfig: CallConfiguration? = null
var callCompositeLocalOptions: CallCompositeLocalOptions? = null
val remoteParticipantsConfiguration: RemoteParticipantsConfiguration =
RemoteParticipantsConfiguration()
/*
CallCompositeConfiguration Storage
The configuration for the call requires callbacks, but these callbacks
can not be passed via intent (not primitive/serializable data).
This is a storage container for Configuration objects, it uses a weak reference
to prevent CallCompositeConfiguration from leaking Activities via it's callbacks.
*/
companion object {
private val configs: HashMap<Int, CallCompositeConfiguration> = HashMap()
// Store a Config by Instance ID
//
// Pass a null configuration to explicitly remove an instance
fun putConfig(id: Int, configuration: CallCompositeConfiguration?) {
if (configuration == null) {
configs.remove(id)
} else {
configs[id] = configuration
}
}
// Gets a config by it's ID
// May return null if the Configuration becomes garbage collected
fun getConfig(id: Int): CallCompositeConfiguration = configs[id]
?: throw CallCompositeException(
"This ID is not valid, and no entry exists in the map. Please file a bug, this is an error in the composite",
IllegalStateException()
)
// Check if config exists
fun hasConfig(id: Int): Boolean = configs[id] != null
}
val remoteParticipantsConfiguration: RemoteParticipantsConfiguration = RemoteParticipantsConfiguration()
}

Просмотреть файл

@ -6,6 +6,7 @@ package com.azure.android.communication.ui.calling.configuration
import com.azure.android.communication.ui.calling.models.CallCompositeParticipantViewData
import com.azure.android.communication.ui.calling.models.CallCompositeSetParticipantViewDataResult
import com.azure.android.communication.ui.calling.service.sdk.CommunicationIdentifier
import java.lang.ref.WeakReference
internal data class RemoteParticipantViewData(
val identifier: CommunicationIdentifier,
@ -18,17 +19,17 @@ internal interface RemoteParticipantsConfigurationHandler {
}
internal class RemoteParticipantsConfiguration {
private var handler: RemoteParticipantsConfigurationHandler? = null
private var handler: WeakReference<RemoteParticipantsConfigurationHandler>? = null
fun setHandler(handler: RemoteParticipantsConfigurationHandler) {
this.handler = handler
this.handler = WeakReference<RemoteParticipantsConfigurationHandler>(handler)
}
fun setParticipantViewData(
identifier: CommunicationIdentifier,
participantViewData: CallCompositeParticipantViewData,
): CallCompositeSetParticipantViewDataResult {
handler?.let {
handler?.get()?.let {
return@setParticipantViewData it.onSetParticipantViewData(
RemoteParticipantViewData(identifier, participantViewData)
)
@ -37,6 +38,6 @@ internal class RemoteParticipantsConfiguration {
}
fun removeParticipantViewData(identifier: String) {
handler?.onRemoveParticipantViewData(identifier)
handler?.get()?.onRemoveParticipantViewData(identifier)
}
}

Просмотреть файл

@ -3,6 +3,7 @@
package com.azure.android.communication.ui.calling.di
import com.azure.android.communication.ui.calling.CallComposite
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration
import com.azure.android.communication.ui.calling.error.ErrorHandler
import com.azure.android.communication.ui.calling.handlers.RemoteParticipantHandler
@ -18,6 +19,7 @@ import com.azure.android.communication.ui.calling.presentation.navigation.Naviga
import com.azure.android.communication.ui.calling.redux.Store
import com.azure.android.communication.ui.calling.redux.middleware.handler.CallingMiddlewareActionHandler
import com.azure.android.communication.ui.calling.redux.state.ReduxState
import com.azure.android.communication.ui.calling.presentation.manager.DiagnosticsManager
import com.azure.android.communication.ui.calling.service.NotificationService
// Dependency Container for the Call Composite Activity
@ -28,6 +30,8 @@ internal interface DependencyInjectionContainer {
val appStore: Store<ReduxState>
val callingMiddlewareActionHandler: CallingMiddlewareActionHandler
val callComposite: CallComposite
// Config
val configuration: CallCompositeConfiguration
val errorHandler: ErrorHandler
@ -43,6 +47,7 @@ internal interface DependencyInjectionContainer {
val notificationService: NotificationService
val audioFocusManager: AudioFocusManager
val networkManager: NetworkManager
val diagnosticsManager: DiagnosticsManager
// UI
val videoViewManager: VideoViewManager

Просмотреть файл

@ -4,8 +4,9 @@
package com.azure.android.communication.ui.calling.di
import android.content.Context
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration
import com.azure.android.communication.ui.calling.CallComposite
import com.azure.android.communication.ui.calling.error.ErrorHandler
import com.azure.android.communication.ui.calling.getConfig
import com.azure.android.communication.ui.calling.handlers.RemoteParticipantHandler
import com.azure.android.communication.ui.calling.logger.DefaultLogger
import com.azure.android.communication.ui.calling.presentation.VideoStreamRendererFactory
@ -42,6 +43,8 @@ import com.azure.android.communication.ui.calling.redux.reducer.Reducer
import com.azure.android.communication.ui.calling.redux.state.AppReduxState
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.calling.presentation.manager.DiagnosticsManager
import com.azure.android.communication.ui.calling.presentation.manager.DiagnosticsManagerImpl
import com.azure.android.communication.ui.calling.service.NotificationService
import com.azure.android.communication.ui.calling.service.sdk.CallingSDK
import com.azure.android.communication.ui.calling.service.sdk.CallingSDKEventHandler
@ -50,15 +53,15 @@ import com.azure.android.communication.ui.calling.utilities.CoroutineContextProv
internal class DependencyInjectionContainerImpl(
private val parentContext: Context,
private val instanceId: Int,
override val callComposite: CallComposite,
private val customCallingSDK: CallingSDK?,
private val customVideoStreamRendererFactory: VideoStreamRendererFactory?,
private val customCoroutineContextProvider: CoroutineContextProvider?
) : DependencyInjectionContainer {
//region Overrides
// These getters are required by the interface
override val configuration get() = CallCompositeConfiguration.getConfig(instanceId)
override val configuration by lazy {
callComposite.getConfig()
}
override val navigationRouter by lazy {
NavigationRouterImpl(appStore)
@ -106,6 +109,11 @@ internal class DependencyInjectionContainerImpl(
applicationContext,
)
}
override val diagnosticsManager: DiagnosticsManager by lazy {
DiagnosticsManagerImpl(
appStore,
)
}
override val avatarViewManager by lazy {
AvatarViewManager(
@ -152,7 +160,7 @@ internal class DependencyInjectionContainerImpl(
//region Redux
// Initial State
private val initialState by lazy { AppReduxState(configuration.callConfig!!.displayName) }
private val initialState by lazy { AppReduxState(configuration.callConfig?.displayName) }
// Reducers
private val callStateReducer get() = CallStateReducerImpl()
@ -196,9 +204,9 @@ internal class DependencyInjectionContainerImpl(
private val callingSDKWrapper: CallingSDK by lazy {
customCallingSDK
?: CallingSDKWrapper(
instanceId,
applicationContext,
callingSDKEventHandler,
configuration.callConfig
)
}

Просмотреть файл

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.ui.calling.models;
/**
* A Call Composite Diagnostics information.
*/
public final class CallCompositeDiagnosticsInfo {
private String lastKnownCallId;
CallCompositeDiagnosticsInfo() { }
/**
* Set last known call id.
* @param lastKnownCallId last known call id.
* @return {@link CallCompositeDiagnosticsInfo}
*/
CallCompositeDiagnosticsInfo setLastKnownCallId(final String lastKnownCallId) {
this.lastKnownCallId = lastKnownCallId;
return this;
}
/**
* Get last known call id.
* @return {@link String}
*/
public String getLastKnownCallId() {
return lastKnownCallId;
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.ui.calling.models
internal fun CallCompositeDiagnosticsInfo.setCallId(lastKnownCallId: String?) {
this.lastKnownCallId = lastKnownCallId
}
internal fun buildCallCompositeDiagnosticsInfo(): CallCompositeDiagnosticsInfo = CallCompositeDiagnosticsInfo()

Просмотреть файл

@ -22,7 +22,7 @@ import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.lifecycleScope
import com.azure.android.communication.ui.R
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration
import com.azure.android.communication.ui.calling.CallCompositeInstanceManager
import com.azure.android.communication.ui.calling.models.CallCompositeSupportedLocale
import com.azure.android.communication.ui.calling.presentation.fragment.calling.CallingFragment
import com.azure.android.communication.ui.calling.presentation.fragment.setup.SetupFragment
@ -61,6 +61,7 @@ internal class CallCompositeActivity : AppCompatActivity() {
private val callingMiddlewareActionHandler get() = container.callingMiddlewareActionHandler
private val videoViewManager get() = container.videoViewManager
private val instanceId get() = intent.getIntExtra(KEY_INSTANCE_ID, -1)
private val diagnosticsService get() = container.diagnosticsManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -116,6 +117,7 @@ internal class CallCompositeActivity : AppCompatActivity() {
}
notificationService.start(lifecycleScope)
diagnosticsService.start(lifecycleScope)
}
override fun onStart() {
@ -139,12 +141,12 @@ internal class CallCompositeActivity : AppCompatActivity() {
// Covers edge case where Android tries to recreate call activity after process death
// (e.g. due to revoked permission).
// If no configs are detected we can just exit without cleanup.
if (CallCompositeConfiguration.hasConfig(instanceId)) {
if (CallCompositeInstanceManager.hasCallComposite(instanceId)) {
audioFocusManager.stop()
audioSessionManager.onDestroy(this)
if (isFinishing) {
store.dispatch(CallingAction.CallEndRequested())
CallCompositeConfiguration.putConfig(instanceId, null)
CallCompositeInstanceManager.removeCallComposite(instanceId)
}
}
super.onDestroy()

Просмотреть файл

@ -7,7 +7,7 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import com.azure.android.communication.ui.R
import com.azure.android.communication.ui.calling.CallCompositeException
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration
import com.azure.android.communication.ui.calling.CallCompositeInstanceManager
import com.azure.android.communication.ui.calling.di.DependencyInjectionContainer
import com.azure.android.communication.ui.calling.di.DependencyInjectionContainerImpl
import com.azure.android.communication.ui.calling.presentation.fragment.calling.CallingViewModel
@ -16,6 +16,7 @@ import com.azure.android.communication.ui.calling.presentation.fragment.factorie
import com.azure.android.communication.ui.calling.presentation.fragment.factories.SetupViewModelFactory
import com.azure.android.communication.ui.calling.presentation.fragment.setup.SetupViewModel
import com.azure.android.communication.ui.calling.service.sdk.CallingSDK
import com.azure.android.communication.ui.calling.setDependencyInjectionContainer
import com.azure.android.communication.ui.calling.utilities.CoroutineContextProvider
import java.lang.IllegalArgumentException
@ -42,7 +43,7 @@ internal class DependencyInjectionContainerHolder(
// Instance ID to locate Configuration. -1 is invalid.
var instanceId: Int = -1
set(value) {
if (!CallCompositeConfiguration.hasConfig(value)) {
if (!CallCompositeInstanceManager.hasCallComposite(value)) {
val exceptionMessage =
"Configuration with instanceId:$value does not exist. $commonMessage"
throw CallCompositeException(exceptionMessage, IllegalArgumentException(exceptionMessage))
@ -57,14 +58,20 @@ internal class DependencyInjectionContainerHolder(
throw CallCompositeException(exceptionMessage, IllegalStateException(exceptionMessage))
}
val callComposite = CallCompositeInstanceManager.getCallComposite(instanceId)
// Generate a new instance
DependencyInjectionContainerImpl(
val container = DependencyInjectionContainerImpl(
application,
instanceId,
callComposite,
customCallingSDK,
customVideoStreamRendererFactory,
customCoroutineContextProvider
)
callComposite.setDependencyInjectionContainer(container)
return@lazy container
}
val setupViewModel by lazy {
@ -80,7 +87,8 @@ internal class DependencyInjectionContainerHolder(
CallingViewModelFactory(
container.appStore,
ParticipantGridCellViewModelFactory(),
application.resources.getInteger(R.integer.azure_communication_ui_calling_max_remote_participants)
application.resources.getInteger(R.integer.azure_communication_ui_calling_max_remote_participants),
container.diagnosticsManager
)
)
}

Просмотреть файл

@ -17,7 +17,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.azure.android.communication.ui.R
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration
import com.azure.android.communication.ui.calling.CallCompositeInstanceManager
import com.azure.android.communication.ui.calling.presentation.DependencyInjectionContainerHolder
import com.azure.android.communication.ui.calling.presentation.fragment.calling.banner.BannerView
import com.azure.android.communication.ui.calling.presentation.fragment.calling.controlbar.ControlBarView
@ -29,6 +29,7 @@ import com.azure.android.communication.ui.calling.presentation.fragment.calling.
import com.azure.android.communication.ui.calling.presentation.fragment.calling.participant.grid.ParticipantGridView
import com.azure.android.communication.ui.calling.presentation.fragment.calling.participantlist.ParticipantListView
import com.azure.android.communication.ui.calling.presentation.fragment.common.audiodevicelist.AudioDeviceListView
import com.azure.android.communication.ui.calling.presentation.fragment.calling.controlbar.more.MoreCallOptionsListView
import com.azure.android.communication.ui.calling.presentation.fragment.setup.components.ErrorInfoView
import com.azure.android.communication.ui.calling.presentation.navigation.BackNavigation
@ -63,13 +64,14 @@ internal class CallingFragment :
private lateinit var powerManager: PowerManager
private lateinit var accessibilityManager: AccessibilityManager
private lateinit var wakeLock: PowerManager.WakeLock
private lateinit var moreCallOptionsListView: MoreCallOptionsListView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.init(viewLifecycleOwner.lifecycleScope)
confirmLeaveOverlayView =
LeaveConfirmView(viewModel.getConfirmLeaveOverlayViewModel(), this.requireContext())
LeaveConfirmView(viewModel.confirmLeaveOverlayViewModel, this.requireContext())
confirmLeaveOverlayView.layoutDirection =
activity?.window?.decorView?.layoutDirection ?: LayoutDirection.LOCALE
confirmLeaveOverlayView.start(
@ -77,17 +79,12 @@ internal class CallingFragment :
)
controlBarView = view.findViewById(R.id.azure_communication_ui_call_call_buttons)
controlBarView.start(
viewLifecycleOwner,
viewModel.getControlBarViewModel(),
this::requestCallEnd,
this::openAudioDeviceSelectionMenu
)
controlBarView.start(viewLifecycleOwner, viewModel.controlBarViewModel)
participantGridView =
view.findViewById(R.id.azure_communication_ui_call_participant_container)
participantGridView.start(
viewModel.getParticipantGridViewModel(),
viewModel.participantGridViewModel,
videoViewManager,
viewLifecycleOwner,
this::switchFloatingHeader,
@ -95,15 +92,15 @@ internal class CallingFragment :
)
lobbyOverlay = view.findViewById(R.id.azure_communication_ui_call_lobby_overlay)
lobbyOverlay.start(viewLifecycleOwner, viewModel.getLobbyOverlayViewModel())
lobbyOverlay.start(viewLifecycleOwner, viewModel.lobbyOverlayViewModel)
holdOverlay = view.findViewById(R.id.azure_communication_ui_call_hold_overlay)
holdOverlay.start(viewLifecycleOwner, viewModel.getHoldOverlayViewModel())
holdOverlay.start(viewLifecycleOwner, viewModel.holdOverlayViewModel)
localParticipantView = view.findViewById(R.id.azure_communication_ui_call_local_user_view)
localParticipantView.start(
viewLifecycleOwner,
viewModel.getLocalParticipantViewModel(),
viewModel.localParticipantViewModel,
videoViewManager,
avatarViewManager,
)
@ -113,19 +110,19 @@ internal class CallingFragment :
infoHeaderView = view.findViewById(R.id.azure_communication_ui_call_floating_header)
infoHeaderView.start(
viewLifecycleOwner,
viewModel.getFloatingHeaderViewModel(),
viewModel.floatingHeaderViewModel,
this::displayParticipantList,
accessibilityManager.isEnabled
)
audioDeviceListView =
AudioDeviceListView(viewModel.getAudioDeviceListViewModel(), this.requireContext())
AudioDeviceListView(viewModel.audioDeviceListViewModel, this.requireContext())
audioDeviceListView.layoutDirection =
activity?.window?.decorView?.layoutDirection ?: LayoutDirection.LOCALE
audioDeviceListView.start(viewLifecycleOwner)
participantListView = ParticipantListView(
viewModel.getParticipantListViewModel(),
viewModel.participantListViewModel,
this.requireContext(),
avatarViewManager,
)
@ -135,7 +132,7 @@ internal class CallingFragment :
bannerView = view.findViewById(R.id.azure_communication_ui_call_banner)
bannerView.start(
viewModel.getBannerViewModel(),
viewModel.bannerViewModel,
viewLifecycleOwner,
)
participantGridView.setOnClickListener {
@ -143,7 +140,15 @@ internal class CallingFragment :
}
errorInfoView = ErrorInfoView(view)
errorInfoView.start(viewLifecycleOwner, viewModel.getErrorInfoViewModel())
errorInfoView.start(viewLifecycleOwner, viewModel.errorInfoViewModel)
moreCallOptionsListView = MoreCallOptionsListView(
this.requireContext(),
viewModel.moreCallOptionsListViewModel
)
moreCallOptionsListView.layoutDirection =
activity?.window?.decorView?.layoutDirection ?: LayoutDirection.LOCALE
moreCallOptionsListView.start(viewLifecycleOwner)
}
override fun onResume() {
@ -177,11 +182,11 @@ internal class CallingFragment :
super.onDestroy()
if (activity?.isChangingConfigurations == false) {
if (this::participantGridView.isInitialized) participantGridView.stop()
if (CallCompositeConfiguration.hasConfig(holder.instanceId)) {
if (CallCompositeInstanceManager.hasCallComposite(holder.instanceId)) {
// Covers edge case where Android tries to recreate call activity after process death
// (e.g. due to revoked permission).
// If no configs are detected we can just exit without cleanup.
viewModel.getBannerViewModel().dismissBanner()
viewModel.bannerViewModel.dismissBanner()
}
}
if (this::localParticipantView.isInitialized) localParticipantView.stop()
@ -190,6 +195,7 @@ internal class CallingFragment :
if (this::confirmLeaveOverlayView.isInitialized) confirmLeaveOverlayView.stop()
if (this::holdOverlay.isInitialized) holdOverlay.stop()
if (this::errorInfoView.isInitialized) errorInfoView.stop()
if (this::moreCallOptionsListView.isInitialized) moreCallOptionsListView.stop()
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
@ -215,9 +221,9 @@ internal class CallingFragment :
override fun onSaveInstanceState(outState: Bundle) {
mapOf(
LEAVE_CONFIRM_VIEW_KEY to viewModel.getConfirmLeaveOverlayViewModel().getShouldDisplayLeaveConfirmFlow(),
AUDIO_DEVICE_LIST_VIEW_KEY to viewModel.getAudioDeviceListViewModel().displayAudioDeviceSelectionMenuStateFlow,
PARTICIPANT_LIST_VIEW_KEY to viewModel.getParticipantListViewModel().getDisplayParticipantListStateFlow()
LEAVE_CONFIRM_VIEW_KEY to viewModel.confirmLeaveOverlayViewModel.getShouldDisplayLeaveConfirmFlow(),
AUDIO_DEVICE_LIST_VIEW_KEY to viewModel.audioDeviceListViewModel.displayAudioDeviceSelectionMenuStateFlow,
PARTICIPANT_LIST_VIEW_KEY to viewModel.participantListViewModel.getDisplayParticipantListStateFlow()
).forEach { (key, element) -> outState.putBoolean(key, element.value) }
super.onSaveInstanceState(outState)
}
@ -227,9 +233,9 @@ internal class CallingFragment :
savedInstanceState?.let {
mapOf(
LEAVE_CONFIRM_VIEW_KEY to viewModel.getConfirmLeaveOverlayViewModel()::requestExitConfirmation,
AUDIO_DEVICE_LIST_VIEW_KEY to viewModel.getAudioDeviceListViewModel()::displayAudioDeviceSelectionMenu,
PARTICIPANT_LIST_VIEW_KEY to viewModel.getParticipantListViewModel()::displayParticipantList
LEAVE_CONFIRM_VIEW_KEY to viewModel.confirmLeaveOverlayViewModel::requestExitConfirmation,
AUDIO_DEVICE_LIST_VIEW_KEY to viewModel.audioDeviceListViewModel::displayAudioDeviceSelectionMenu,
PARTICIPANT_LIST_VIEW_KEY to viewModel.participantListViewModel::displayParticipantList
).forEach { (key, showDialog) -> if (it.getBoolean(key)) showDialog() }
}
}
@ -238,12 +244,8 @@ internal class CallingFragment :
viewModel.requestCallEnd()
}
private fun openAudioDeviceSelectionMenu() {
viewModel.getAudioDeviceListViewModel().displayAudioDeviceSelectionMenu()
}
private fun displayParticipantList() {
viewModel.getParticipantListViewModel().displayParticipantList()
viewModel.participantListViewModel.displayParticipantList()
}
private fun switchFloatingHeader() {

Просмотреть файл

@ -4,16 +4,6 @@
package com.azure.android.communication.ui.calling.presentation.fragment.calling
import com.azure.android.communication.ui.calling.presentation.fragment.BaseViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.banner.BannerViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.controlbar.ControlBarViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.hangup.LeaveConfirmViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.header.InfoHeaderViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.hold.OnHoldOverlayViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.lobby.LobbyOverlayViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.localuser.LocalParticipantViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.participant.grid.ParticipantGridViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.participantlist.ParticipantListViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.common.audiodevicelist.AudioDeviceListViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.factories.CallingViewModelFactory
import com.azure.android.communication.ui.calling.redux.Store
import com.azure.android.communication.ui.calling.redux.state.CallingStatus
@ -27,64 +17,18 @@ internal class CallingViewModel(
) :
BaseViewModel(store) {
private val participantGridViewModel =
callingViewModelProvider.participantGridViewModel
private val controlBarViewModel = callingViewModelProvider.controlBarViewModel
private val confirmLeaveOverlayViewModel =
callingViewModelProvider.confirmLeaveOverlayViewModel
private val localParticipantViewModel =
callingViewModelProvider.localParticipantViewModel
private val floatingHeaderViewModel = callingViewModelProvider.floatingHeaderViewModel
private val audioDeviceListViewModel =
callingViewModelProvider.audioDeviceListViewModel
private val participantListViewModel =
callingViewModelProvider.participantListViewModel
private val bannerViewModel = callingViewModelProvider.bannerViewModel
private val lobbyOverlayViewModel = callingViewModelProvider.lobbyOverlayViewModel
private val holdOverlayViewModel = callingViewModelProvider.onHoldOverlayViewModel
private val errorInfoViewModel = callingViewModelProvider.snackBarViewModel
fun getLobbyOverlayViewModel(): LobbyOverlayViewModel {
return lobbyOverlayViewModel
}
fun getHoldOverlayViewModel(): OnHoldOverlayViewModel {
return holdOverlayViewModel
}
fun getParticipantGridViewModel(): ParticipantGridViewModel {
return participantGridViewModel
}
fun getAudioDeviceListViewModel(): AudioDeviceListViewModel {
return audioDeviceListViewModel
}
fun getControlBarViewModel(): ControlBarViewModel {
return controlBarViewModel
}
fun getConfirmLeaveOverlayViewModel(): LeaveConfirmViewModel {
return confirmLeaveOverlayViewModel
}
fun getLocalParticipantViewModel(): LocalParticipantViewModel {
return localParticipantViewModel
}
fun getFloatingHeaderViewModel(): InfoHeaderViewModel {
return floatingHeaderViewModel
}
fun getParticipantListViewModel(): ParticipantListViewModel {
return participantListViewModel
}
fun getBannerViewModel(): BannerViewModel {
return bannerViewModel
}
fun getErrorInfoViewModel() = errorInfoViewModel
val moreCallOptionsListViewModel = callingViewModelProvider.moreCallOptionsListViewModel
val participantGridViewModel = callingViewModelProvider.participantGridViewModel
val controlBarViewModel = callingViewModelProvider.controlBarViewModel
val confirmLeaveOverlayViewModel = callingViewModelProvider.confirmLeaveOverlayViewModel
val localParticipantViewModel = callingViewModelProvider.localParticipantViewModel
val floatingHeaderViewModel = callingViewModelProvider.floatingHeaderViewModel
val audioDeviceListViewModel = callingViewModelProvider.audioDeviceListViewModel
val participantListViewModel = callingViewModelProvider.participantListViewModel
val bannerViewModel = callingViewModelProvider.bannerViewModel
val lobbyOverlayViewModel = callingViewModelProvider.lobbyOverlayViewModel
val holdOverlayViewModel = callingViewModelProvider.onHoldOverlayViewModel
val errorInfoViewModel = callingViewModelProvider.snackBarViewModel
fun switchFloatingHeader() {
floatingHeaderViewModel.switchFloatingHeader()
@ -100,7 +44,10 @@ internal class CallingViewModel(
controlBarViewModel.init(
state.permissionState,
state.localParticipantState.cameraState,
state.localParticipantState.audioState
state.localParticipantState.audioState,
this::requestCallEnd,
audioDeviceListViewModel::displayAudioDeviceSelectionMenu,
moreCallOptionsListViewModel::display,
)
localParticipantViewModel.init(

Просмотреть файл

@ -33,8 +33,7 @@ internal class ControlBarView : ConstraintLayout {
private lateinit var cameraToggle: ImageButton
private lateinit var micToggle: ImageButton
private lateinit var callAudioDeviceButton: ImageButton
private lateinit var requestCallEndCallback: () -> Unit
private lateinit var openAudioDeviceSelectionMenuCallback: () -> Unit
private lateinit var moreButton: ImageButton
override fun onFinishInflate() {
super.onFinishInflate()
@ -42,18 +41,16 @@ internal class ControlBarView : ConstraintLayout {
cameraToggle = findViewById(R.id.azure_communication_ui_call_cameraToggle)
micToggle = findViewById(R.id.azure_communication_ui_call_call_audio)
callAudioDeviceButton = findViewById(R.id.azure_communication_ui_call_audio_device_button)
moreButton = findViewById(R.id.azure_communication_ui_call_control_bar_more)
subscribeClickListener()
}
fun start(
viewLifecycleOwner: LifecycleOwner,
viewModel: ControlBarViewModel,
requestCallEnd: () -> Unit,
openAudioDeviceSelectionMenu: () -> Unit,
) {
this.viewModel = viewModel
this.requestCallEndCallback = requestCallEnd
this.openAudioDeviceSelectionMenuCallback = openAudioDeviceSelectionMenu
setupAccessibility()
viewLifecycleOwner.lifecycleScope.launch {
@ -200,7 +197,7 @@ internal class ControlBarView : ConstraintLayout {
private fun subscribeClickListener() {
endCallButton.setOnClickListener {
requestCallEndCallback()
viewModel.requestCallEnd()
}
micToggle.setOnClickListener {
if (micToggle.isSelected) {
@ -217,7 +214,10 @@ internal class ControlBarView : ConstraintLayout {
}
}
callAudioDeviceButton.setOnClickListener {
openAudioDeviceSelectionMenuCallback()
viewModel.openAudioDeviceSelectionMenu()
}
moreButton.setOnClickListener {
viewModel.openMoreMenu()
}
}
}

Просмотреть файл

@ -21,11 +21,17 @@ internal class ControlBarViewModel(private val dispatch: (Action) -> Unit) {
private lateinit var audioDeviceSelectionStatusStateFlow: MutableStateFlow<AudioDeviceSelectionStatus>
private lateinit var shouldEnableMicButtonStateFlow: MutableStateFlow<Boolean>
private lateinit var onHoldCallStatusStateFlow: MutableStateFlow<Boolean>
lateinit var requestCallEnd: () -> Unit
lateinit var openAudioDeviceSelectionMenu: () -> Unit
lateinit var openMoreMenu: () -> Unit
fun init(
permissionState: PermissionState,
cameraState: CameraState,
audioState: AudioState,
requestCallEndCallback: () -> Unit,
openAudioDeviceSelectionMenuCallback: () -> Unit,
openMoreMenuCallback: () -> Unit
) {
cameraStateFlow =
MutableStateFlow(CameraModel(permissionState.cameraPermissionState, cameraState))
@ -34,6 +40,9 @@ internal class ControlBarViewModel(private val dispatch: (Action) -> Unit) {
shouldEnableMicButtonStateFlow =
MutableStateFlow(shouldEnableMicButton(audioState))
onHoldCallStatusStateFlow = MutableStateFlow(false)
requestCallEnd = requestCallEndCallback
openAudioDeviceSelectionMenu = openAudioDeviceSelectionMenuCallback
openMoreMenu = openMoreMenuCallback
}
fun update(

Просмотреть файл

@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.ui.calling.presentation.fragment.calling.controlbar.more
import android.content.Context
import android.content.Intent
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.azure.android.communication.ui.R
import com.azure.android.communication.ui.calling.utilities.BottomCellAdapter
import com.azure.android.communication.ui.calling.utilities.BottomCellItem
import com.microsoft.fluentui.drawer.DrawerDialog
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
internal class MoreCallOptionsListView(
context: Context,
private val viewModel: MoreCallOptionsListViewModel
) : RelativeLayout(context) {
private var recyclerView: RecyclerView
private lateinit var menuDrawer: DrawerDialog
private lateinit var bottomCellAdapter: BottomCellAdapter
init {
inflate(context, R.layout.azure_communication_ui_calling_listview, this)
recyclerView = findViewById(R.id.bottom_drawer_table)
this.setBackgroundResource(R.color.azure_communication_ui_calling_color_bottom_drawer_background)
}
fun start(viewLifecycleOwner: LifecycleOwner) {
initializeDrawer()
viewLifecycleOwner.lifecycleScope.launch {
viewModel.displayStateFlow.collect {
if (it) {
menuDrawer.show()
}
}
}
}
fun stop() {
bottomCellAdapter.setBottomCellItems(mutableListOf())
recyclerView.layoutManager = null
menuDrawer.dismiss()
menuDrawer.dismissDialog()
this.removeAllViews()
}
private fun initializeDrawer() {
menuDrawer = DrawerDialog(context, DrawerDialog.BehaviorType.BOTTOM)
menuDrawer.setContentView(this)
menuDrawer.setOnDismissListener {
viewModel.close()
}
bottomCellAdapter = BottomCellAdapter()
bottomCellAdapter.setBottomCellItems(bottomCellItems)
recyclerView.adapter = bottomCellAdapter
recyclerView.layoutManager = LinearLayoutManager(context)
}
private val bottomCellItems: List<BottomCellItem>
get() {
val bottomCellItems = listOf(
BottomCellItem(
icon = ContextCompat.getDrawable(
context,
R.drawable.azure_communication_ui_calling_ic_fluent_share_android_24_regular
),
title = context.getString(R.string.azure_communication_ui_calling_view_share_diagnostics),
contentDescription = null,
accessoryImage = null,
accessoryColor = null,
accessoryImageDescription = context.getString(R.string.azure_communication_ui_calling_view_share_diagnostics),
enabled = false,
participantViewData = null,
isOnHold = false,
) {
menuDrawer.dismiss()
shareDiagnosticsInfo()
},
)
return bottomCellItems
}
private fun shareDiagnosticsInfo() {
val share = Intent.createChooser(
Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, viewModel.callId)
type = "text/plain"
putExtra(Intent.EXTRA_TITLE, context.getString(R.string.azure_communication_ui_calling_view_share_diagnostics_title))
},
null
)
context.startActivity(share)
}
}

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.ui.calling.presentation.fragment.calling.controlbar.more
import com.azure.android.communication.ui.calling.presentation.manager.DiagnosticsManager
import kotlinx.coroutines.flow.MutableStateFlow
internal class MoreCallOptionsListViewModel(
private val diagnosticsManager: DiagnosticsManager
) {
private val unknown = "UNKNOWN"
val callId: String
get() {
val lastKnownCallId = diagnosticsManager.diagnosticsInfo.lastKnownCallId
return "Call ID: \"${if (lastKnownCallId.isNullOrEmpty()) unknown else lastKnownCallId}\""
}
val displayStateFlow = MutableStateFlow(false)
fun display() {
displayStateFlow.value = true
}
fun close() {
displayStateFlow.value = false
}
}

Просмотреть файл

@ -13,7 +13,9 @@ import com.azure.android.communication.ui.calling.presentation.fragment.calling.
import com.azure.android.communication.ui.calling.presentation.fragment.calling.participant.grid.ParticipantGridViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.participantlist.ParticipantListViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.common.audiodevicelist.AudioDeviceListViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.controlbar.more.MoreCallOptionsListViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.setup.components.ErrorInfoViewModel
import com.azure.android.communication.ui.calling.presentation.manager.DiagnosticsManager
import com.azure.android.communication.ui.calling.redux.Store
import com.azure.android.communication.ui.calling.redux.state.ReduxState
@ -21,8 +23,13 @@ internal class CallingViewModelFactory(
private val store: Store<ReduxState>,
private val participantGridCellViewModelFactory: ParticipantGridCellViewModelFactory,
private val maxRemoteParticipants: Int,
private val diagnosticsManager: DiagnosticsManager
) {
val moreCallOptionsListViewModel by lazy {
MoreCallOptionsListViewModel(diagnosticsManager)
}
val participantGridViewModel by lazy {
ParticipantGridViewModel(participantGridCellViewModelFactory, maxRemoteParticipants)
}

Просмотреть файл

@ -55,39 +55,39 @@ internal class SetupFragment :
setActionBarTitle()
setupGradientView = view.findViewById(R.id.azure_communication_ui_setup_gradient)
setupGradientView.start(viewLifecycleOwner, viewModel.getSetupGradientViewViewModel())
setupGradientView.start(viewLifecycleOwner, viewModel.setupGradientViewModel)
setupJoinCallButtonHolderView =
view.findViewById(R.id.azure_communication_ui_setup_join_call_holder)
setupJoinCallButtonHolderView.start(
viewLifecycleOwner,
viewModel.getJoinCallButtonHolderViewModel(),
viewModel.joinCallButtonHolderViewModel,
networkManager
)
participantAvatarView = view.findViewById(R.id.azure_communication_ui_setup_default_avatar)
participantAvatarView.start(
viewLifecycleOwner,
viewModel.getParticipantAvatarViewModel(),
viewModel.participantAvatarViewModel,
avatarViewManager.callCompositeLocalOptions?.participantViewData,
)
warningsView = view.findViewById(R.id.azure_communication_ui_setup_permission_info)
warningsView.start(
viewLifecycleOwner,
viewModel.getWarningsViewModel(),
viewModel.warningsViewModel,
)
localParticipantRendererView =
view.findViewById(R.id.azure_communication_ui_setup_local_video_holder)
localParticipantRendererView.start(
viewLifecycleOwner,
viewModel.getLocalParticipantRendererViewModel(),
viewModel.localParticipantRendererViewModel,
videoViewManager,
)
audioDeviceListView =
AudioDeviceListView(viewModel.getAudioDeviceListViewModel(), this.requireContext())
AudioDeviceListView(viewModel.audioDeviceListViewModel, this.requireContext())
audioDeviceListView.layoutDirection =
activity?.window?.decorView?.layoutDirection ?: LayoutDirection.LOCALE
audioDeviceListView.start(viewLifecycleOwner)
@ -95,11 +95,11 @@ internal class SetupFragment :
setupControlsView = view.findViewById(R.id.azure_communication_ui_setup_buttons)
setupControlsView.start(
viewLifecycleOwner,
viewModel.getSetupControlsViewModel()
viewModel.setupControlsViewModel,
)
errorInfoView = ErrorInfoView(view)
errorInfoView.start(viewLifecycleOwner, viewModel.getErrorInfoViewModel())
errorInfoView.start(viewLifecycleOwner, viewModel.errorInfoViewModel)
viewModel.setupCall()
}

Просмотреть файл

@ -17,30 +17,14 @@ internal class SetupViewModel(
) :
BaseViewModel(store) {
private val warningsViewModel = setupViewModelProvider.warningsViewModel
private val setupControlsViewModel = setupViewModelProvider.setupControlsViewModel
private val localParticipantRendererViewModel = setupViewModelProvider.previewAreaViewModel
private val audioDeviceListViewModel = setupViewModelProvider.audioDeviceListViewModel
private val errorInfoViewModel = setupViewModelProvider.snackBarViewModel
private val setupGradientViewModel = setupViewModelProvider.setupGradientViewModel
private val participantAvatarViewModel = setupViewModelProvider.participantAvatarViewModel
private val joinCallButtonHolderViewModel = setupViewModelProvider.joinCallButtonHolderViewModel
fun getJoinCallButtonHolderViewModel() = joinCallButtonHolderViewModel
fun getParticipantAvatarViewModel() = participantAvatarViewModel
fun getSetupGradientViewViewModel() = setupGradientViewModel
fun getErrorInfoViewModel() = errorInfoViewModel
fun getLocalParticipantRendererViewModel() = localParticipantRendererViewModel
fun getAudioDeviceListViewModel() = audioDeviceListViewModel
fun getWarningsViewModel() = warningsViewModel
fun getSetupControlsViewModel() = setupControlsViewModel
val warningsViewModel = setupViewModelProvider.warningsViewModel
val setupControlsViewModel = setupViewModelProvider.setupControlsViewModel
val localParticipantRendererViewModel = setupViewModelProvider.previewAreaViewModel
val audioDeviceListViewModel = setupViewModelProvider.audioDeviceListViewModel
val errorInfoViewModel = setupViewModelProvider.snackBarViewModel
val setupGradientViewModel = setupViewModelProvider.setupGradientViewModel
val participantAvatarViewModel = setupViewModelProvider.participantAvatarViewModel
val joinCallButtonHolderViewModel = setupViewModelProvider.joinCallButtonHolderViewModel
val displayName: String?
get() = store.getCurrentState().localParticipantState.displayName
@ -56,7 +40,7 @@ internal class SetupViewModel(
override fun init(coroutineScope: CoroutineScope) {
val state = store.getCurrentState()
getWarningsViewModel().init(state.permissionState)
warningsViewModel.init(state.permissionState)
localParticipantRendererViewModel.init(
state.localParticipantState.videoStreamID,
)
@ -95,7 +79,7 @@ internal class SetupViewModel(
state.localParticipantState.audioState,
state.callState,
)
getWarningsViewModel().update(state.permissionState)
warningsViewModel.update(state.permissionState)
localParticipantRendererViewModel.update(
state.localParticipantState.videoStreamID,
)

Просмотреть файл

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.ui.calling.presentation.manager
import com.azure.android.communication.ui.calling.models.CallCompositeDiagnosticsInfo
import com.azure.android.communication.ui.calling.models.buildCallCompositeDiagnosticsInfo
import com.azure.android.communication.ui.calling.models.setCallId
import com.azure.android.communication.ui.calling.redux.Store
import com.azure.android.communication.ui.calling.redux.state.ReduxState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
internal interface DiagnosticsManager {
fun start(coroutineScope: CoroutineScope)
val diagnosticsInfo: CallCompositeDiagnosticsInfo
}
internal class DiagnosticsManagerImpl(
private val store: Store<ReduxState>,
) : DiagnosticsManager {
override var diagnosticsInfo = buildCallCompositeDiagnosticsInfo()
override fun start(coroutineScope: CoroutineScope) {
coroutineScope.launch {
store.getStateFlow().collect {
if (!it.callState.callId.isNullOrEmpty()) {
val newDiagnosticsInfo = buildCallCompositeDiagnosticsInfo()
newDiagnosticsInfo.setCallId(it.callState.callId)
diagnosticsInfo = newDiagnosticsInfo
}
}
}
}
}

Просмотреть файл

@ -14,4 +14,5 @@ internal sealed class CallingAction : Action {
class StateUpdated(val callingState: CallingStatus) : CallingAction()
class IsRecordingUpdated(val isRecording: Boolean) : CallingAction()
class IsTranscribingUpdated(val isTranscribing: Boolean) : CallingAction()
class CallIdUpdated(val callId: String?) : CallingAction()
}

Просмотреть файл

@ -206,6 +206,7 @@ internal class CallingMiddlewareActionHandlerImpl(
subscribeIsRecordingUpdate(store)
subscribeIsTranscribingUpdate(store)
subscribeCallInfoModelEventUpdate(store)
subscribeCallIdUpdate(store)
subscribeCamerasCountUpdate(store)
callingService.startCall(
@ -390,6 +391,14 @@ internal class CallingMiddlewareActionHandlerImpl(
}
}
private fun subscribeCallIdUpdate(store: Store<ReduxState>) {
coroutineScope.launch {
callingService.getCallIdStateFlow().collect {
store.dispatch(CallingAction.CallIdUpdated(it))
}
}
}
private fun tryCameraOn(store: Store<ReduxState>) {
val state = store.getCurrentState()
if (state.localParticipantState.cameraState.operation == CameraOperationalStatus.PAUSED) {

Просмотреть файл

@ -24,6 +24,9 @@ internal class CallStateReducerImpl : CallStateReducer {
is CallingAction.CallStartRequested -> {
callingState.copy(joinCallIsRequested = true)
}
is CallingAction.CallIdUpdated -> {
callingState.copy(callId = action.callId)
}
else -> callingState
}
}

Просмотреть файл

@ -18,6 +18,7 @@ internal enum class CallingStatus {
internal data class CallingState(
val callingStatus: CallingStatus,
var callId: String? = null,
// due to the async nature of the CallingStatus update we need to indicate joining call
// until we receive CallingStatus.CONNECTING from the SDK.
val joinCallIsRequested: Boolean = false,

Просмотреть файл

@ -18,6 +18,7 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@ -37,6 +38,7 @@ internal class CallingService(
private val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
private val coroutineScope = CoroutineScope((coroutineContextProvider.Default))
private var callInfoModelSharedFlow = MutableSharedFlow<CallInfoModel>()
private var callIdStateFlow = MutableStateFlow<String?>(null)
private var callingStatus: CallingStatus = CallingStatus.NONE
fun turnCameraOn(): CompletableFuture<String> {
@ -86,6 +88,8 @@ internal class CallingService(
fun getCallInfoModelEventSharedFlow(): SharedFlow<CallInfoModel> = callInfoModelSharedFlow
fun getCallIdStateFlow(): SharedFlow<String?> = callIdStateFlow
fun getIsTranscribingSharedFlow(): Flow<Boolean> {
return isTranscribingSharedFlow
}
@ -123,6 +127,12 @@ internal class CallingService(
}
}
coroutineScope.launch {
callingSdk.getCallIdStateFlow().collect {
callIdStateFlow.emit(it)
}
}
coroutineScope.launch {
callingSdk.getIsMutedSharedFlow().collect {
isMutedSharedFlow.emit(it)

Просмотреть файл

@ -46,6 +46,7 @@ internal interface CallingSDK {
fun getIsRecordingSharedFlow(): SharedFlow<Boolean>
fun getIsMutedSharedFlow(): SharedFlow<Boolean>
fun getCallingStateWrapperSharedFlow(): SharedFlow<CallingStateWrapper>
fun getCallIdStateFlow(): StateFlow<String?>
fun getRemoteParticipantInfoModelSharedFlow(): Flow<Map<String, ParticipantInfoModel>>
fun getCamerasCountStateFlow(): StateFlow<Int>
}

Просмотреть файл

@ -21,7 +21,9 @@ import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch
@ -38,6 +40,7 @@ internal class CallingSDKEventHandler(
private var isRecordingSharedFlow = MutableSharedFlow<Boolean>()
private var isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
private var callingStateWrapperSharedFlow = MutableSharedFlow<CallingStateWrapper>()
private var callIdSharedFlow = MutableStateFlow<String?>(null)
private var remoteParticipantsInfoModelSharedFlow =
MutableSharedFlow<Map<String, ParticipantInfoModel>>()
@ -59,6 +62,8 @@ internal class CallingSDKEventHandler(
fun getCallingStateWrapperSharedFlow(): SharedFlow<CallingStateWrapper> =
callingStateWrapperSharedFlow
fun getCallIdStateFlow(): StateFlow<String?> = callIdSharedFlow
fun getIsMutedSharedFlow(): SharedFlow<Boolean> = isMutedSharedFlow
fun getIsRecordingSharedFlow(): SharedFlow<Boolean> = isRecordingSharedFlow
@ -132,6 +137,10 @@ internal class CallingSDKEventHandler(
}
private fun onCallStateChange() {
coroutineScope.launch {
callIdSharedFlow.emit(call?.id)
}
val callState = call?.state
var callEndStatus = Pair(0, 0)
@ -337,6 +346,7 @@ internal class CallingSDKEventHandler(
isRecordingSharedFlow = MutableSharedFlow()
isTranscribingSharedFlow = MutableSharedFlow()
callingStateWrapperSharedFlow = MutableSharedFlow()
callIdSharedFlow = MutableStateFlow(null)
remoteParticipantsInfoModelSharedFlow = MutableSharedFlow()
}
}

Просмотреть файл

@ -21,7 +21,6 @@ import com.azure.android.communication.calling.TeamsMeetingLinkLocator
import com.azure.android.communication.calling.VideoDevicesUpdatedListener
import com.azure.android.communication.calling.VideoOptions
import com.azure.android.communication.ui.calling.CallCompositeException
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration
import com.azure.android.communication.ui.calling.configuration.CallConfiguration
import com.azure.android.communication.ui.calling.configuration.CallType
import com.azure.android.communication.ui.calling.logger.Logger
@ -35,11 +34,12 @@ import com.azure.android.communication.ui.calling.service.sdk.ext.setTags
import java9.util.concurrent.CompletableFuture
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
internal class CallingSDKWrapper(
private val instanceId: Int,
private val context: Context,
private val callingSDKEventHandler: CallingSDKEventHandler,
private val callConfigInjected: CallConfiguration?,
private val logger: Logger? = null,
) : CallingSDK {
private var nullableCall: Call? = null
@ -52,14 +52,13 @@ internal class CallingSDKWrapper(
private var camerasInitializedCompletableFuture: CompletableFuture<Void>? = null
private var setupCallCompletableFuture: CompletableFuture<Void> = CompletableFuture()
private val configuration get() = CallCompositeConfiguration.getConfig(instanceId)
private var videoDevicesUpdatedListener: VideoDevicesUpdatedListener? = null
private var camerasCountStateFlow = MutableStateFlow(0)
private val callConfig: CallConfiguration
get() {
try {
return configuration.callConfig!!
return callConfigInjected!!
} catch (ex: Exception) {
throw CallCompositeException(
"Call configurations are not set",
@ -91,6 +90,8 @@ internal class CallingSDKWrapper(
override fun getCallingStateWrapperSharedFlow() =
callingSDKEventHandler.getCallingStateWrapperSharedFlow()
override fun getCallIdStateFlow(): StateFlow<String?> = callingSDKEventHandler.getCallIdStateFlow()
override fun getIsMutedSharedFlow() = callingSDKEventHandler.getIsMutedSharedFlow()
override fun getIsRecordingSharedFlow() = callingSDKEventHandler.getIsRecordingSharedFlow()
@ -150,7 +151,7 @@ internal class CallingSDKWrapper(
override fun setupCall(): CompletableFuture<Void> {
if (callClient == null) {
val callClientOptions = CallClientOptions().also {
it.setTags(configuration.callConfig?.diagnosticConfig?.tags, logger)
it.setTags(callConfig.diagnosticConfig.tags, logger)
}
callClient = CallClient(callClientOptions)
}

Просмотреть файл

@ -0,0 +1,15 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
>
<path
android:fillColor="@color/azure_communication_ui_calling_color_on_surface"
android:pathData="M17 2.498c1.934 0 3.502 1.568 3.502 3.502 0 1.934-1.568 3.502-3.502 3.502-1.03 0-1.956-0.445-2.597-1.153l-4.558 2.604c0.103 0.33 0.159 0.682 0.159 1.047 0 0.364-0.056 0.716-0.16 1.046l4.558 2.606c0.641-0.709 1.568-1.154 2.598-1.154 1.934 0 3.502 1.568 3.502 3.502 0 1.934-1.568 3.502-3.502 3.502-1.934 0-3.502-1.568-3.502-3.502 0-0.365 0.056-0.716 0.16-1.047L9.1 14.348c-0.641 0.708-1.568 1.154-2.598 1.154C4.568 15.502 3 13.934 3 12c0-1.934 1.568-3.502 3.502-3.502 1.03 0 1.956 0.445 2.597 1.152l4.558-2.604C13.554 6.716 13.498 6.364 13.498 6c0-1.934 1.568-3.502 3.502-3.502zm0 13.5c-1.105 0-2.002 0.896-2.002 2.002 0 1.105 0.897 2.002 2.002 2.002 1.105 0 2.002-0.897 2.002-2.002 0-1.106-0.896-2.002-2.002-2.002zm-10.498-6C5.396 9.998 4.5 10.894 4.5 12c0 1.105 0.896 2.002 2.002 2.002 1.105 0 2.002-0.897 2.002-2.002 0-1.106-0.897-2.002-2.002-2.002zm10.498-6c-1.105 0-2.002 0.896-2.002 2.002 0 1.105 0.897 2.002 2.002 2.002 1.105 0 2.002-0.897 2.002-2.002 0-1.106-0.896-2.002-2.002-2.002z"
/>
</vector>

Просмотреть файл

@ -17,18 +17,34 @@
style="@style/Widget.FluentUI.Button"
android:layout_width="60dp"
android:layout_height="60dp"
android:accessibilityTraversalAfter="@id/azure_communication_ui_call_audio_device_button"
android:accessibilityTraversalAfter="@id/azure_communication_ui_call_control_bar_more"
android:background="@drawable/azure_communication_ui_calling_corner_radius_rectangle_4dp"
android:backgroundTint="@color/azure_communication_ui_calling_color_background_red"
android:contentDescription="@string/azure_communication_ui_calling_view_button_hang_up_accessibility_label"
android:src="@drawable/azure_communication_ui_calling_ic_fluent_call_end_28_filled_composite_button_filled"
app:layout_constraintBottom_toTopOf="@+id/azure_communication_ui_call_audio_device_button"
app:layout_constraintBottom_toTopOf="@+id/azure_communication_ui_call_control_bar_more"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<ImageButton
android:id="@+id/azure_communication_ui_call_control_bar_more"
style="@style/Widget.FluentUI.Button"
android:layout_width="60dp"
android:layout_height="60dp"
android:accessibilityTraversalAfter="@id/azure_communication_ui_call_audio_device_button"
android:background="@color/azure_communication_ui_calling_color_button_background_transparent"
android:contentDescription="@string/azure_communication_ui_calling_view_more_menu_accessibility_label"
android:src="@drawable/azure_communication_ui_calling_ic_fluent_dot_button_22_composite_button_filled"
app:layout_constraintBottom_toTopOf="@+id/azure_communication_ui_call_audio_device_button"
app:layout_constraintTop_toBottomOf="@+id/azure_communication_ui_call_end_call_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<ImageButton
android:id="@+id/azure_communication_ui_call_audio_device_button"
style="@style/Widget.FluentUI.Button"
@ -39,7 +55,7 @@
android:contentDescription="@string/azure_communication_ui_calling_view_button_device_options_accessibility_label"
android:src="@drawable/azure_communication_ui_calling_ic_fluent_speaker_2_24_regular_composite_button_filled"
app:layout_constraintBottom_toTopOf="@+id/azure_communication_ui_call_call_audio"
app:layout_constraintTop_toBottomOf="@+id/azure_communication_ui_call_end_call_button"
app:layout_constraintTop_toBottomOf="@+id/azure_communication_ui_call_control_bar_more"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>

Просмотреть файл

@ -51,26 +51,42 @@
android:background="@color/azure_communication_ui_calling_color_button_background_transparent"
android:contentDescription="@string/azure_communication_ui_calling_view_button_device_options_accessibility_label"
android:src="@drawable/azure_communication_ui_calling_ic_fluent_speaker_2_24_regular_composite_button_filled"
app:layout_constraintEnd_toStartOf="@+id/azure_communication_ui_call_end_call_button"
app:layout_constraintEnd_toStartOf="@+id/azure_communication_ui_call_control_bar_more"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/azure_communication_ui_call_call_audio"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<ImageButton
android:id="@+id/azure_communication_ui_call_control_bar_more"
style="@style/Widget.FluentUI.Button"
android:layout_width="60dp"
android:layout_height="60dp"
android:accessibilityTraversalAfter="@id/azure_communication_ui_call_audio_device_button"
android:background="@color/azure_communication_ui_calling_color_button_background_transparent"
android:contentDescription="@string/azure_communication_ui_calling_view_more_menu_accessibility_label"
android:src="@drawable/azure_communication_ui_calling_ic_fluent_dot_button_22_composite_button_filled"
app:layout_constraintEnd_toStartOf="@+id/azure_communication_ui_call_end_call_button"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/azure_communication_ui_call_audio_device_button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<ImageButton
android:id="@+id/azure_communication_ui_call_end_call_button"
style="@style/Widget.FluentUI.Button"
android:layout_width="60dp"
android:layout_height="60dp"
android:accessibilityTraversalAfter="@id/azure_communication_ui_call_audio_device_button"
android:accessibilityTraversalAfter="@id/azure_communication_ui_call_control_bar_more"
android:background="@drawable/azure_communication_ui_calling_corner_radius_rectangle_4dp"
android:backgroundTint="@color/azure_communication_ui_calling_color_background_red"
android:contentDescription="@string/azure_communication_ui_calling_view_button_hang_up_accessibility_label"
android:src="@drawable/azure_communication_ui_calling_ic_fluent_call_end_28_filled_composite_button_filled"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/azure_communication_ui_call_audio_device_button"
app:layout_constraintStart_toEndOf="@+id/azure_communication_ui_call_control_bar_more"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>

Просмотреть файл

@ -47,13 +47,29 @@
android:background="@color/azure_communication_ui_calling_color_button_background_transparent"
android:contentDescription="@string/azure_communication_ui_calling_view_button_device_options_accessibility_label"
android:src="@drawable/azure_communication_ui_calling_ic_fluent_speaker_2_24_regular_composite_button_filled"
app:layout_constraintEnd_toStartOf="@+id/azure_communication_ui_call_end_call_button"
app:layout_constraintEnd_toStartOf="@+id/azure_communication_ui_call_control_bar_more"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/azure_communication_ui_call_call_audio"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<ImageButton
android:id="@+id/azure_communication_ui_call_control_bar_more"
style="@style/Widget.FluentUI.Button"
android:layout_width="60dp"
android:layout_height="60dp"
android:accessibilityTraversalAfter="@id/azure_communication_ui_call_audio_device_button"
android:background="@color/azure_communication_ui_calling_color_button_background_transparent"
android:contentDescription="@string/azure_communication_ui_calling_view_more_menu_accessibility_label"
android:src="@drawable/azure_communication_ui_calling_ic_fluent_dot_button_22_composite_button_filled"
app:layout_constraintEnd_toStartOf="@+id/azure_communication_ui_call_end_call_button"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/azure_communication_ui_call_audio_device_button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<ImageButton
android:id="@+id/azure_communication_ui_call_end_call_button"
style="@style/Widget.FluentUI.Button"
@ -65,7 +81,7 @@
android:src="@drawable/azure_communication_ui_calling_ic_fluent_call_end_28_filled_composite_button_filled"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/azure_communication_ui_call_audio_device_button"
app:layout_constraintStart_toEndOf="@+id/azure_communication_ui_call_control_bar_more"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Auf Video umschalten</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Auf Audio umschalten</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Geräteoptionen</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Die Transkription hat begonnen.</b> Indem Sie sich anmelden, erklären Sie

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Auf Video umschalten</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Auf Audio umschalten</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Geräteoptionen</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Die Transkription hat begonnen.</b> Indem Sie sich anmelden, erklären Sie

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Toggle Video</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Toggle Audio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Device Options</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Recording has started.</b> By joining, you are

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Toggle Video</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Toggle Audio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Device Options</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Recording has started.</b> By joining, you are

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Activar/desactivar vídeo</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Activar/desactivar audio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Opciones de dispositivo</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Se ha iniciado la grabación.</b> Al unirse, está

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Activar/desactivar vídeo</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Activar/desactivar audio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Opciones de dispositivo</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Se ha iniciado la grabación.</b> Al unirse, está

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Activer/désactiver la vidéo</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Activer/désactiver laudio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Options du périphérique</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>La transcription a démarré.</b> En participant, vous

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Activer/désactiver la vidéo</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Activer/désactiver laudio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Options du périphérique</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>La transcription a démarré.</b> En participant, vous

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Attiva/disattiva video</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Attiva/Disattiva audio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Opzioni dispositivo</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>La registrazione è stata avviata.</b> Partecipando,

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Attiva/disattiva video</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Attiva/Disattiva audio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Opzioni dispositivo</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>La registrazione è stata avviata.</b> Partecipando,

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">ビデオの切り替え</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">オーディオの切り替え</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">デバイス オプション</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>レコーディングが開始されました。</b> 参加すると、

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">ビデオの切り替え</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">オーディオの切り替え</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">デバイス オプション</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>レコーディングが開始されました。</b> 参加すると、

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">비디오 설정/해제</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">오디오 설정/해제</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">디바이스 옵션</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>녹음을 시작했습니다.</b>참가하면 이 모임을 전사하는데

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">비디오 설정/해제</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">오디오 설정/해제</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">디바이스 옵션</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>녹음을 시작했습니다.</b>참가하면 이 모임을 전사하는데

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Video in-/uitschakelen</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Audio in-/uitschakelen</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Apparaatopties</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Opname is gestart.</b> Door deel te nemen geeft

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Video in-/uitschakelen</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Audio in-/uitschakelen</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Apparaatopties</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Opname is gestart.</b> Door deel te nemen geeft

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Ativar/Desativar Vídeo</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Ativar/Desativar Áudio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Opções do Dispositivo</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>A transcrição foi iniciada.</b> Ingressando, você está

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Ativar/Desativar Vídeo</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Ativar/Desativar Áudio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Opções do Dispositivo</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>A transcrição foi iniciada.</b> Ingressando, você está

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Включить или отключить видео</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Включить или отключить звук</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Параметры устройства</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Началась запись.</b> Присоединяясь, вы

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Включить или отключить видео</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Включить или отключить звук</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Параметры устройства</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Началась запись.</b> Присоединяясь, вы

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Görüntüyü Aç/Kapat</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Sesi Aç/Kapat</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Cihaz Seçenekleri</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Kayıt başlatıldı.</b> Katılarak,

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Görüntüyü Aç/Kapat</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Sesi Aç/Kapat</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Cihaz Seçenekleri</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Kayıt başlatıldı.</b> Katılarak,

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">切换视频</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">切换音频</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">设备选项</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>录制已开始。</b>加入即表示你

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">切換視訊</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">切換音訊</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">裝置選項</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>錄製已開始。</b>一旦加入,即表示您

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">切换视频</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">切换音频</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">设备选项</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>录制已开始。</b>加入即表示你

Просмотреть файл

@ -60,6 +60,9 @@
<string name="azure_communication_ui_calling_view_button_toggle_video_accessibility_label">Toggle Video</string>
<string name="azure_communication_ui_calling_view_button_toggle_audio_accessibility_label">Toggle Audio</string>
<string name="azure_communication_ui_calling_view_button_device_options_accessibility_label">Device Options</string>
<string name="azure_communication_ui_calling_view_more_menu_accessibility_label">More</string>
<string name="azure_communication_ui_calling_view_share_diagnostics">Share diagnostics info</string>
<string name="azure_communication_ui_calling_view_share_diagnostics_title">Diagnostics info</string>
<!-- Compliance Banner-->
<string name="azure_communication_ui_calling_view_banner_recording_started"><b>Recording has started.</b> By joining, you are

Просмотреть файл

@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.ui
import com.azure.android.communication.ui.calling.CallCompositeBuilder
import com.azure.android.communication.ui.calling.CallCompositeException
import com.azure.android.communication.ui.calling.CallCompositeInstanceManager
import org.hamcrest.MatcherAssert
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
internal class CallCompositeInstanceManagerTest {
@Test
fun callCompositeInstanceManager_errorHandling() {
val classObj = CallCompositeException::class.java
CallCompositeInstanceManager.putCallComposite(0, CallCompositeBuilder().build())
val ex = Assert.assertThrows(classObj, ::getCallCompositeConfig)
MatcherAssert.assertThat(
"invalid type: ${ex.javaClass.simpleName}",
ex.javaClass.simpleName == classObj.simpleName
)
MatcherAssert.assertThat(
"invalid message, expecting: ${ex.message}",
ex.message?.startsWith("This ID is not valid, and no entry exists in the map")
?: false
)
MatcherAssert.assertThat(
"Invalid cause, expecting: ${ex.cause}",
ex.cause is IllegalStateException
)
}
private fun getCallCompositeConfig() = CallCompositeInstanceManager.getCallComposite(1)
}

Просмотреть файл

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.ui.configuration
import com.azure.android.communication.ui.calling.CallCompositeException
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
internal class CallCompositeExceptionTest {
@Test
fun callCompositeConfiguration_errorHandling() {
val classObj = CallCompositeException::class.java
CallCompositeConfiguration.putConfig(0, CallCompositeConfiguration())
val ex = assertThrows(classObj, ::getCallCompositeConfig)
assertThat(
"invalid type: ${ex.javaClass.simpleName}",
ex.javaClass.simpleName == classObj.simpleName
)
assertThat(
"invalid message, expecting: ${ex.message}",
ex.message?.startsWith("This ID is not valid, and no entry exists in the map") ?: false
)
assertThat(
"Invalid cause, expecting: ${ex.cause}",
ex.cause is IllegalStateException
)
}
private fun getCallCompositeConfig() = CallCompositeConfiguration.getConfig(1)
}

Просмотреть файл

@ -31,6 +31,7 @@ import com.azure.android.communication.ui.calling.redux.state.AudioOperationalSt
import com.azure.android.communication.ui.calling.redux.state.AudioDeviceSelectionStatus
import com.azure.android.communication.ui.ACSBaseTestCoroutine
import com.azure.android.communication.ui.calling.presentation.fragment.calling.controlbar.more.MoreCallOptionsListViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.hold.OnHoldOverlayViewModel
import com.azure.android.communication.ui.calling.presentation.fragment.calling.lobby.LobbyOverlayViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -85,6 +86,7 @@ internal class CallingViewModelUnitTest : ACSBaseTestCoroutine() {
val mockLobbyOverlayViewModel = mock<LobbyOverlayViewModel>()
val mockOnHoldOverlayViewModel = mock<OnHoldOverlayViewModel>()
val mockMoreCallOptionsListViewModel = mock<MoreCallOptionsListViewModel>()
val mockCallingViewModelProvider = mock<CallingViewModelFactory> {
on { participantGridViewModel } doAnswer { mockParticipantGridViewModel }
@ -97,6 +99,7 @@ internal class CallingViewModelUnitTest : ACSBaseTestCoroutine() {
on { bannerViewModel } doAnswer { mockBannerViewModel }
on { lobbyOverlayViewModel } doAnswer { mockLobbyOverlayViewModel }
on { onHoldOverlayViewModel } doAnswer { mockOnHoldOverlayViewModel }
on { moreCallOptionsListViewModel } doAnswer { mockMoreCallOptionsListViewModel }
}
val callingViewModel = CallingViewModel(
@ -167,6 +170,7 @@ internal class CallingViewModelUnitTest : ACSBaseTestCoroutine() {
val mockLobbyOverlayViewModel = mock<LobbyOverlayViewModel>()
val mockOnHoldOverlayViewModel = mock<OnHoldOverlayViewModel>()
val mockMoreCallOptionsListViewModel = mock<MoreCallOptionsListViewModel>()
val mockCallingViewModelProvider = mock<CallingViewModelFactory> {
on { participantGridViewModel } doAnswer { mockParticipantGridViewModel }
@ -179,6 +183,7 @@ internal class CallingViewModelUnitTest : ACSBaseTestCoroutine() {
on { bannerViewModel } doAnswer { mockBannerViewModel }
on { lobbyOverlayViewModel } doAnswer { mockLobbyOverlayViewModel }
on { onHoldOverlayViewModel } doAnswer { mockOnHoldOverlayViewModel }
on { moreCallOptionsListViewModel } doAnswer { mockMoreCallOptionsListViewModel }
}
val callingViewModel = CallingViewModel(
@ -248,6 +253,7 @@ internal class CallingViewModelUnitTest : ACSBaseTestCoroutine() {
val mockBannerViewModel = mock<BannerViewModel>()
val mockLobbyOverlayViewModel = mock<LobbyOverlayViewModel>()
val mockOnHoldOverlayViewModel = mock<OnHoldOverlayViewModel>()
val mockMoreCallOptionsListViewModel = mock<MoreCallOptionsListViewModel>()
val mockCallingViewModelProvider = mock<CallingViewModelFactory> {
on { participantGridViewModel } doAnswer { mockParticipantGridViewModel }
@ -260,6 +266,7 @@ internal class CallingViewModelUnitTest : ACSBaseTestCoroutine() {
on { bannerViewModel } doAnswer { mockBannerViewModel }
on { lobbyOverlayViewModel } doAnswer { mockLobbyOverlayViewModel }
on { onHoldOverlayViewModel } doAnswer { mockOnHoldOverlayViewModel }
on { moreCallOptionsListViewModel } doAnswer { mockMoreCallOptionsListViewModel }
}
val callingViewModel = CallingViewModel(
@ -336,6 +343,7 @@ internal class CallingViewModelUnitTest : ACSBaseTestCoroutine() {
val mockBannerViewModel = mock<BannerViewModel>()
val mockLobbyOverlayViewModel = mock<LobbyOverlayViewModel>()
val mockOnHoldOverlayViewModel = mock<OnHoldOverlayViewModel>()
val mockMoreCallOptionsListViewModel = mock<MoreCallOptionsListViewModel>()
val mockCallingViewModelProvider = mock<CallingViewModelFactory> {
on { participantGridViewModel } doAnswer { mockParticipantGridViewModel }
@ -348,6 +356,7 @@ internal class CallingViewModelUnitTest : ACSBaseTestCoroutine() {
on { bannerViewModel } doAnswer { mockBannerViewModel }
on { lobbyOverlayViewModel } doAnswer { mockLobbyOverlayViewModel }
on { onHoldOverlayViewModel } doAnswer { mockOnHoldOverlayViewModel }
on { moreCallOptionsListViewModel } doAnswer { mockMoreCallOptionsListViewModel }
}
val callingViewModel = CallingViewModel(

Просмотреть файл

@ -124,7 +124,10 @@ internal class ControlBarViewModelUnitTest : ACSBaseTestCoroutine() {
AudioOperationalStatus.OFF,
audioDeviceState,
BluetoothState(available = false, deviceName = "bluetooth")
)
),
{},
{},
{},
)
val expectedAudioOperationalStatus1 = AudioOperationalStatus.ON
@ -208,7 +211,10 @@ internal class ControlBarViewModelUnitTest : ACSBaseTestCoroutine() {
AudioOperationalStatus.OFF,
audioDeviceState,
BluetoothState(available = false, deviceName = "bluetooth")
)
),
{},
{},
{},
)
val flowJob = launch {
@ -296,7 +302,10 @@ internal class ControlBarViewModelUnitTest : ACSBaseTestCoroutine() {
AudioOperationalStatus.OFF,
audioDeviceState,
BluetoothState(available = false, deviceName = "bluetooth")
)
),
{},
{},
{},
)
val resultListFromCameraStateFlow = mutableListOf<ControlBarViewModel.CameraModel>()
@ -377,7 +386,10 @@ internal class ControlBarViewModelUnitTest : ACSBaseTestCoroutine() {
AudioOperationalStatus.OFF,
audioDeviceState,
BluetoothState(available = false, deviceName = "bluetooth")
)
),
{},
{},
{},
)
val resultListFromOnHoldCallStatusStateFlow = mutableListOf<Boolean>()

Просмотреть файл

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.android.communication.ui.presentation.manager
import com.azure.android.communication.ui.ACSBaseTestCoroutine
import com.azure.android.communication.ui.calling.presentation.manager.DiagnosticsManager
import com.azure.android.communication.ui.calling.presentation.manager.DiagnosticsManagerImpl
import com.azure.android.communication.ui.calling.redux.AppStore
import com.azure.android.communication.ui.calling.redux.state.AppReduxState
import com.azure.android.communication.ui.calling.redux.state.CallingState
import com.azure.android.communication.ui.calling.redux.state.CallingStatus
import com.azure.android.communication.ui.calling.redux.state.ReduxState
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
@RunWith(MockitoJUnitRunner::class)
internal class DiagnosticsManagerTest : ACSBaseTestCoroutine() {
@Test
@ExperimentalCoroutinesApi
fun diagnosticsManager_getCallId_returns_callId() {
runScopedTest {
// arrange
val appState1 = AppReduxState("")
val stateFlow = MutableStateFlow<ReduxState>(appState1)
val mockAppStore = mock<AppStore<ReduxState>> {
on { getStateFlow() } doAnswer { stateFlow }
}
val diagnosticsManager: DiagnosticsManager = DiagnosticsManagerImpl(mockAppStore)
val flowJob = launch {
diagnosticsManager.start(coroutineScope = this)
}
val diagnosticsInfo1 = diagnosticsManager.diagnosticsInfo
Assert.assertNotNull(diagnosticsInfo1)
Assert.assertNull(diagnosticsInfo1.lastKnownCallId)
// update state
val appState2 = AppReduxState("")
val callID = "callID"
appState2.callState = CallingState(CallingStatus.CONNECTING, callID)
stateFlow.value = appState2
val diagnosticsInfo2 = diagnosticsManager.diagnosticsInfo
Assert.assertNotSame(diagnosticsInfo1, diagnosticsInfo2)
Assert.assertNotNull(diagnosticsInfo2)
Assert.assertEquals(callID, diagnosticsInfo2.lastKnownCallId)
// redux state loosing CallID
// update state
val appState3 = AppReduxState("")
appState3.callState = CallingState(CallingStatus.CONNECTING, null)
stateFlow.value = appState3
val diagnosticsInfo3 = diagnosticsManager.diagnosticsInfo
Assert.assertSame(diagnosticsInfo2, diagnosticsInfo3)
Assert.assertNotNull(diagnosticsInfo3)
Assert.assertEquals(callID, diagnosticsInfo3.lastKnownCallId)
flowJob.cancel()
}
}
}

Просмотреть файл

@ -365,6 +365,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -386,6 +387,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val mockCallingService: CallingService = mock {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -442,6 +444,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
MutableSharedFlow()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -450,6 +453,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val mockCallingService: CallingService = mock {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -542,6 +546,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
@ -551,6 +556,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn startCallCompletableFuture
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
@ -611,6 +617,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
@ -620,6 +627,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn startCallCompletableFuture
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
@ -673,6 +681,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -682,6 +691,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn startCallCompletableFuture
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -735,6 +745,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -744,6 +755,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn startCallCompletableFuture
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
@ -797,6 +809,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
@ -805,6 +818,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn startCallCompletableFuture
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -1428,6 +1442,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -1437,6 +1452,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -1499,6 +1515,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow =
MutableStateFlow(CallInfoModel(CallingStatus.LOCAL_HOLD, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -1508,7 +1525,9 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val mockCallingService: CallingService = mock {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallIdStateFlow() } doReturn callIdFlow
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -1551,6 +1570,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -1560,6 +1580,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -1618,6 +1639,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val callIdFlow = MutableStateFlow<String?>(null)
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
val camerasCountUpdatedStateFlow = MutableStateFlow(2)
@ -1626,6 +1648,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -1679,6 +1702,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -1688,6 +1712,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -1745,6 +1770,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -1754,6 +1780,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -1809,6 +1836,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -1818,6 +1846,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -1889,6 +1918,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -1898,6 +1928,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -1957,6 +1988,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val callingServiceParticipantsSharedFlow =
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow = MutableStateFlow(CallInfoModel(CallingStatus.NONE, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -1966,6 +1998,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow
@ -2049,6 +2082,7 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
MutableSharedFlow<MutableMap<String, ParticipantInfoModel>>()
val callInfoModelStateFlow =
MutableStateFlow(CallInfoModel(CallingStatus.LOCAL_HOLD, null))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -2058,7 +2092,9 @@ internal class CallingMiddlewareActionHandlerUnitTest : ACSBaseTestCoroutine() {
val mockCallingService: CallingService = mock {
on { getParticipantsInfoModelSharedFlow() } doReturn callingServiceParticipantsSharedFlow
on { startCall(any(), any()) } doReturn CompletableFuture<Void>()
on { getCallIdStateFlow() } doReturn callIdFlow
on { getCallInfoModelEventSharedFlow() } doReturn callInfoModelStateFlow
on { getCallIdStateFlow() } doReturn callIdFlow
on { getIsMutedSharedFlow() } doReturn isMutedSharedFlow
on { getIsRecordingSharedFlow() } doReturn isRecordingSharedFlow
on { getIsTranscribingSharedFlow() } doReturn isTranscribingSharedFlow

Просмотреть файл

@ -60,6 +60,7 @@ internal class CallingServiceUnitTests : ACSBaseTestCoroutine() {
val callingStateWrapperStateFlow =
MutableStateFlow(CallingStateWrapper(callState, 0))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -68,6 +69,8 @@ internal class CallingServiceUnitTests : ACSBaseTestCoroutine() {
.thenReturn(remoteParticipantsInfoModelSharedFlow)
Mockito.`when`(mockCallingGateway.getCallingStateWrapperSharedFlow())
.thenReturn(callingStateWrapperStateFlow)
Mockito.`when`(mockCallingGateway.getCallIdStateFlow())
.thenReturn(callIdFlow)
Mockito.`when`(mockCallingGateway.getIsMutedSharedFlow())
.thenReturn(isMutedSharedFlow)
Mockito.`when`(mockCallingGateway.getIsRecordingSharedFlow())
@ -273,6 +276,7 @@ internal class CallingServiceUnitTests : ACSBaseTestCoroutine() {
val callingStateWrapperStateFlow =
MutableStateFlow(CallingStateWrapper(CallState.NONE, 0))
val callIdFlow = MutableStateFlow<String?>(null)
val isMutedSharedFlow = MutableSharedFlow<Boolean>()
val isRecordingSharedFlow = MutableSharedFlow<Boolean>()
val isTranscribingSharedFlow = MutableSharedFlow<Boolean>()
@ -281,7 +285,8 @@ internal class CallingServiceUnitTests : ACSBaseTestCoroutine() {
.thenReturn(remoteParticipantsInfoModelSharedFlow)
Mockito.`when`(mockCallingGateway.getRemoteParticipantInfoModelSharedFlow())
.thenReturn(remoteParticipantsInfoModelSharedFlow)
Mockito.`when`(mockCallingGateway.getCallIdStateFlow())
.thenReturn(callIdFlow)
Mockito.`when`(mockCallingGateway.getCallingStateWrapperSharedFlow())
.thenReturn(callingStateWrapperStateFlow)
Mockito.`when`(mockCallingGateway.getIsMutedSharedFlow())

Просмотреть файл

@ -18,16 +18,16 @@ class CallingCompositeAudioDeviceListTest : BaseUiTest() {
@Test
fun selectDefaultAudioDevice() {
joinGroupSetupScreen()
.selectAndroidAudioDevice(false)
.verifyIsAndroidAudioDevice()
.selectSpeakerAudioDevice(true)
.verifyIsSpeakerAudioDevice()
.navigateUpFromSetupScreen()
}
@Test
fun selectSpeakerAudioDevice() {
fun selectAndroidAudioDevice() {
joinGroupSetupScreen()
.selectSpeakerAudioDevice(true)
.verifyIsSpeakerAudioDevice()
.selectAndroidAudioDevice(false)
.verifyIsAndroidAudioDevice()
.navigateUpFromSetupScreen()
}

Просмотреть файл

@ -3,6 +3,7 @@
package com.azure.android.communication.ui.callingcompositedemoapp
import com.azure.android.communication.ui.calling.CallComposite
import com.azure.android.communication.ui.calling.CallCompositeEventHandler
import com.azure.android.communication.ui.calling.models.CallCompositeErrorEvent
import java.lang.ref.WeakReference
@ -10,7 +11,10 @@ import java.lang.ref.WeakReference
// Handles forwarding of error messages to the CallLauncherActivity
//
// CallLauncherActivity is loosely coupled and will detach the weak reference after disposed.
class CallLauncherActivityErrorHandler(callLauncherActivity: CallLauncherActivity) :
class CallLauncherActivityErrorHandler(
private val callComposite: CallComposite,
callLauncherActivity: CallLauncherActivity
) :
CallCompositeEventHandler<CallCompositeErrorEvent> {
private val activityWr: WeakReference<CallLauncherActivity> =
@ -18,9 +22,10 @@ class CallLauncherActivityErrorHandler(callLauncherActivity: CallLauncherActivit
override fun handle(it: CallCompositeErrorEvent) {
println("================= application is logging exception =================")
println("call id: " + (callComposite.diagnosticsInfo.lastKnownCallId ?: ""))
println(it.cause)
println(it.errorCode)
activityWr.get()?.showAlert(it.errorCode.toString() + " " + it.cause?.message)
activityWr.get()?.showAlert("${it.errorCode} ${it.cause?.message}. Call id: ${callComposite.diagnosticsInfo.lastKnownCallId ?: ""}")
println("====================================================================")
}
}

Просмотреть файл

@ -29,6 +29,7 @@ import kotlin.Unit;
import kotlin.jvm.functions.Function1;
public class CallingCompositeJavaLauncher implements CallingCompositeLauncher {
private static CallComposite callComposite;
private final Callable<String> tokenRefresher;
public CallingCompositeJavaLauncher(final Callable<String> tokenRefresher) {
@ -57,7 +58,7 @@ public class CallingCompositeJavaLauncher implements CallingCompositeLauncher {
}
final CallComposite callComposite = builder.build();
callComposite.addOnErrorEventHandler(new CallLauncherActivityErrorHandler(callLauncherActivity));
callComposite.addOnErrorEventHandler(new CallLauncherActivityErrorHandler(callComposite, callLauncherActivity));
if (SettingsFeatures.getRemoteParticipantPersonaInjectionSelection()) {
callComposite.addOnRemoteParticipantJoinedEventHandler(
@ -84,5 +85,8 @@ public class CallingCompositeJavaLauncher implements CallingCompositeLauncher {
.setTitleAndSubtitle(SettingsFeatures.getTitle(), SettingsFeatures.getSubtitle()));
callComposite.launch(callLauncherActivity, remoteOptions, localOptions);
// For test purposes we will keep a static ref to CallComposite
this.callComposite = callComposite;
}
}

Просмотреть файл

@ -44,7 +44,7 @@ class CallingCompositeKotlinLauncher(private val tokenRefresher: Callable<String
val selectedLanguage = language()
val locale = selectedLanguage?.let { locale(it) }
val callComposite: CallComposite =
val callComposite =
if (AdditionalFeatures.secondaryThemeFeature.active)
CallCompositeBuilder().theme(R.style.MyCompany_Theme_Calling)
.localization(CallCompositeLocalizationOptions(locale!!, getLayoutDirection()))
@ -54,7 +54,7 @@ class CallingCompositeKotlinLauncher(private val tokenRefresher: Callable<String
.localization(CallCompositeLocalizationOptions(locale!!, getLayoutDirection()))
.build()
callComposite.addOnErrorEventHandler(CallLauncherActivityErrorHandler(callLauncherActivity))
callComposite.addOnErrorEventHandler(CallLauncherActivityErrorHandler(callComposite, callLauncherActivity))
if (getRemoteParticipantPersonaInjectionSelection()) {
callComposite.addOnRemoteParticipantJoinedEventHandler(
@ -82,5 +82,12 @@ class CallingCompositeKotlinLauncher(private val tokenRefresher: Callable<String
)
callComposite.launch(callLauncherActivity, remoteOptions, localOptions)
// For test purposes we will keep a static ref to CallComposite
CallingCompositeKotlinLauncher.callComposite = callComposite
}
companion object {
var callComposite: CallComposite? = null
}
}