Краткое руководство. Присоединение вызывающего приложения к собранию Teams
Из этого краткого руководства вы узнаете, как присоединиться к собранию Teams с помощью пакета SDK для вызова Службы коммуникации Azure для JavaScript.
Пример кода
Итоговый код для этого краткого руководства можно найти на сайте GitHub.
Необходимые компоненты
- Рабочее веб-приложение, вызывающее Службы коммуникации Azure.
- Развертывание Teams.
- Минимальная версия, поддерживаемая для API присоединения к собраниям Teams и секретного кода: 1.17.1
Добавление элементов управления пользовательского интерфейса Teams
Замените код в index.html приведенным ниже фрагментом кода. Присоединяйтесь к собранию Teams с помощью ссылки на собрания Teams или Teams MeetingId и секретный код. Текстовые поля используются для ввода контекста собрания Teams, а кнопка используется для присоединения к указанному собранию:
<!DOCTYPE html>
<html>
<head>
<title>Communication Client - Calling Sample</title>
</head>
<body>
<h4>Azure Communication Services</h4>
<h1>Teams meeting join quickstart</h1>
<input id="teams-link-input" type="text" placeholder="Teams meeting link"
style="margin-bottom:1em; width: 300px;" />
<p><input id="teams-meetingId-input" type="text" placeholder="Teams meetingId"
style="margin-bottom:1em; width: 300px;" /></p>
<p><input id="teams-passcode-input" type="text" placeholder="Teams meeting Passcode"
style="margin-bottom:1em; width: 300px;" /></p>
<p>Call state <span style="font-weight: bold" id="call-state">-</span></p>
<p><span style="font-weight: bold" id="recording-state"></span></p>
<div>
<button id="join-meeting-button" type="button" disabled="false">
Join Teams Meeting
</button>
<button id="hang-up-button" type="button" disabled="true">
Hang Up
</button>
</div>
<script src="./app.js" type="module"></script>
</body>
</html>
Включение элементов управления пользовательского интерфейса Teams
Замените содержимое файла app.js следующим фрагментом кода.
import { CallClient } from "@azure/communication-calling";
import { Features } from "@azure/communication-calling";
import { AzureCommunicationTokenCredential } from '@azure/communication-common';
let call;
let callAgent;
const meetingLinkInput = document.getElementById('teams-link-input');
const meetingIdInput = document.getElementById('teams-meetingId-input');
const meetingPasscodeInput = document.getElementById('teams-passcode-input');
const hangUpButton = document.getElementById('hang-up-button');
const teamsMeetingJoinButton = document.getElementById('join-meeting-button');
const callStateElement = document.getElementById('call-state');
const recordingStateElement = document.getElementById('recording-state');
async function init() {
const callClient = new CallClient();
const tokenCredential = new AzureCommunicationTokenCredential("<USER ACCESS TOKEN>");
callAgent = await callClient.createCallAgent(tokenCredential, {displayName: 'Test user'});
teamsMeetingJoinButton.disabled = false;
}
init();
hangUpButton.addEventListener("click", async () => {
// end the current call
await call.hangUp();
// toggle button states
hangUpButton.disabled = true;
teamsMeetingJoinButton.disabled = false;
callStateElement.innerText = '-';
});
teamsMeetingJoinButton.addEventListener("click", () => {
// join with meeting link
call = callAgent.join({meetingLink: meetingLinkInput.value}, {});
//(or) to join with meetingId and passcode use the below code snippet.
//call = callAgent.join({meetingId: meetingIdInput.value, passcode: meetingPasscodeInput.value}, {});
call.on('stateChanged', () => {
callStateElement.innerText = call.state;
})
call.api(Features.Recording).on('isRecordingActiveChanged', () => {
if (call.api(Features.Recording).isRecordingActive) {
recordingStateElement.innerText = "This call is being recorded";
}
else {
recordingStateElement.innerText = "";
}
});
// toggle button states
hangUpButton.disabled = false;
teamsMeetingJoinButton.disabled = true;
});
Получение ссылки на собрание Teams
Ссылку на собрание Teams можно получить с помощью интерфейсов API Graph. Это действие подробно описано в документации по Graph.
Пакет SDK вызовов Служб коммуникации принимает полную ссылку на собрание Teams. Эта ссылка возвращается как часть ресурса onlineMeeting
, доступного в свойстве joinWebUrl
. Кроме того, вы можете получить необходимые сведения о собрании, воспользовавшись URL-адресом для участия в собрании в самом приглашении на собрание Teams.
Получение идентификатора собрания Teams и секретного кода
- API Graph: используйте API Graph для получения сведений о ресурсе OnlineMeeting и проверка объекта в свойстве joinMeetingId Параметры.
- Teams: в приложении Teams перейдите в приложение "Календарь" и откройте сведения о собрании. В онлайн-собраниях есть идентификатор собрания и секретный код в определении собрания.
- Outlook: вы можете найти секретный код идентификатора & собрания в событиях календаря или в приглашениях на собрание электронной почты.
Выполнение кода
Выполните следующую команду, чтобы создать пакет узла приложения на локальном веб-сервере:
npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map
Откройте браузер и перейдите к http://localhost:8080/. Вы увидите следующее:
Вставьте контекст Teams в текстовое поле и нажмите Присоединиться к собранию Teams, чтобы присоединиться к собранию Teams из приложения Служб коммуникации Azure.
В этом кратком руководстве вы узнаете, как присоединиться к собранию Teams с помощью пакета SDK вызова Служб коммуникации Azure для Windows.
Необходимые компоненты
Добавление и включение элементов управления пользовательского интерфейса Teams
Замените код в MainPage.xaml следующим фрагментом. Текстовое поле будет использоваться для ввода контекста собрания Teams, а кнопка — для присоединения к указанному собранию:
<Page
x:Class="CallingQuickstart.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CallingQuickstart"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Width="800" Height="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="16*"/>
<RowDefinition Height="30*"/>
<RowDefinition Height="200*"/>
<RowDefinition Height="60*"/>
<RowDefinition Height="16*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="1" x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="10,10,10,10" />
<Grid x:Name="AppTitleBar" Background="LightSeaGreen">
<!-- Width of the padding columns is set in LayoutMetricsChanged handler. -->
<!-- Using padding columns instead of Margin ensures that the background paints the area under the caption control buttons (for transparent buttons). -->
<TextBlock x:Name="QuickstartTitle" Text="Calling Quickstart sample title bar" Style="{StaticResource CaptionTextBlockStyle}" Padding="7,7,0,0"/>
</Grid>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<MediaPlayerElement x:Name="LocalVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="0" VerticalAlignment="Center" AutoPlay="True" />
<MediaPlayerElement x:Name="RemoteVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="1" VerticalAlignment="Center" AutoPlay="True" />
</Grid>
<StackPanel Grid.Row="3" Orientation="Vertical" Grid.RowSpan="2">
<StackPanel Orientation="Horizontal">
<Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
<Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
</StackPanel>
</StackPanel>
<TextBox Grid.Row="5" x:Name="Stats" Text="" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="0,2,0,0" BorderThickness="2" IsReadOnly="True" Foreground="LightSlateGray" />
</Grid>
</Page>
Включение элементов управления пользовательского интерфейса Teams
Замените содержимое файла MainPage.xaml.cs
приведенным ниже фрагментом:
using Azure.Communication.Calling.WindowsClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Core;
using Windows.Media.Core;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace CallingQuickstart
{
public sealed partial class MainPage : Page
{
private const string authToken = "<AUTHENTICATION_TOKEN>";
private CallClient callClient;
private CallTokenRefreshOptions callTokenRefreshOptions = new CallTokenRefreshOptions(false);
private CallAgent callAgent;
private CommunicationCall call;
private LocalOutgoingAudioStream micStream;
private LocalOutgoingVideoStream cameraStream;
#region Page initialization
public MainPage()
{
this.InitializeComponent();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
await InitCallAgentAndDeviceManagerAsync();
base.OnNavigatedTo(e);
}
#endregion
#region UI event handlers
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
if (!string.IsNullOrEmpty(callString))
{
call = await JoinTeamsMeetingByLinkAsync(teamsMeetinglink);
}
if (call != null)
{
call.RemoteParticipantsUpdated += OnRemoteParticipantsUpdatedAsync;
call.StateChanged += OnStateChangedAsync;
}
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
foreach (var localVideoStream in call.OutgoingVideoStreams)
{
await call.StopVideoAsync(localVideoStream);
}
if (cameraStream != null)
{
await cameraStream.StopPreviewAsync();
}
await call.HangUpAsync(new HangUpOptions() { ForEveryone = false });
}
}
#endregion
#region API event handlers
private async void OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
{
var call = sender as CommunicationCall;
if (call != null)
{
var state = call.State;
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
QuickstartTitle.Text = $"{Package.Current.DisplayName} - {state.ToString()}";
Window.Current.SetTitleBar(AppTitleBar);
HangupButton.IsEnabled = state == CallState.Connected || state == CallState.Ringing;
CallButton.IsEnabled = !HangupButton.IsEnabled;
});
switch (state)
{
case CallState.Connected:
{
await call.StartAudioAsync(micStream);
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
Stats.Text = $"Call id: {Guid.Parse(call.Id).ToString("D")}, Remote caller id: {call.RemoteParticipants.FirstOrDefault()?.Identifier.RawId}";
});
break;
}
case CallState.Disconnected:
{
call.RemoteParticipantsUpdated -= OnRemoteParticipantsUpdatedAsync;
call.StateChanged -= OnStateChangedAsync;
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
Stats.Text = $"Call ended: {call.CallEndReason.ToString()}";
});
call.Dispose();
break;
}
default: break;
}
}
}
private async void OnRemoteParticipantsUpdatedAsync(object sender, ParticipantsUpdatedEventArgs args)
{
await OnParticipantChangedAsync(
args.RemovedParticipants.ToList<RemoteParticipant>(),
args.AddedParticipants.ToList<RemoteParticipant>());
}
private async Task OnParticipantChangedAsync(IEnumerable<RemoteParticipant> removedParticipants, IEnumerable<RemoteParticipant> addedParticipants)
{
foreach (var participant in removedParticipants)
{
foreach(var incomingVideoStream in participant.IncomingVideoStreams)
{
var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
if (remoteVideoStream != null)
{
await remoteVideoStream.StopPreviewAsync();
}
}
participant.VideoStreamStateChanged -= OnVideoStreamStateChanged;
}
foreach (var participant in addedParticipants)
{
participant.VideoStreamStateChanged += OnVideoStreamStateChanged;
}
}
private void OnVideoStreamStateChanged(object sender, VideoStreamStateChangedEventArgs e)
{
CallVideoStream callVideoStream = e.Stream;
switch (callVideoStream.Direction)
{
case StreamDirection.Outgoing:
OnOutgoingVideoStreamStateChanged(callVideoStream as OutgoingVideoStream);
break;
case StreamDirection.Incoming:
OnIncomingVideoStreamStateChangedAsync(callVideoStream as IncomingVideoStream);
break;
}
}
private async void OnIncomingVideoStreamStateChangedAsync(IncomingVideoStream incomingVideoStream)
{
switch (incomingVideoStream.State)
{
case VideoStreamState.Available:
switch (incomingVideoStream.Kind)
{
case VideoStreamKind.RemoteIncoming:
var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
var uri = await remoteVideoStream.StartPreviewAsync();
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
RemoteVideo.Source = MediaSource.CreateFromUri(uri);
});
break;
case VideoStreamKind.RawIncoming:
break;
}
break;
case VideoStreamState.Started:
break;
case VideoStreamState.Stopping:
case VideoStreamState.Stopped:
if (incomingVideoStream.Kind == VideoStreamKind.RemoteIncoming)
{
var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
await remoteVideoStream.StopPreviewAsync();
}
break;
case VideoStreamState.NotAvailable:
break;
}
}
#endregion
#region Helpers
private async Task InitCallAgentAndDeviceManagerAsync()
{
this.callClient = new CallClient(new CallClientOptions() {
Diagnostics = new CallDiagnosticsOptions() {
AppName = "CallingQuickstart",
AppVersion="1.0",
Tags = new[] { "Calling", "ACS", "Windows" }
}
});
// Set up local video stream using the first camera enumerated
var deviceManager = await this.callClient.GetDeviceManagerAsync();
var camera = deviceManager?.Cameras?.FirstOrDefault();
var mic = deviceManager?.Microphones?.FirstOrDefault();
micStream = new LocalOutgoingAudioStream();
if (camera != null)
{
cameraStream = new LocalOutgoingVideoStream(selectedCamerea);
var localUri = await cameraStream.StartPreviewAsync();
LocalVideo.Source = MediaSource.CreateFromUri(localUri);
if (call != null) {
await call?.StartVideoAsync(cameraStream);
}
}
var tokenCredential = new CallTokenCredential(authToken, callTokenRefreshOptions);
var callAgentOptions = new CallAgentOptions()
{
DisplayName = $"{Environment.MachineName}/{Environment.UserName}",
};
this.callAgent = await this.callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
// Sets up additional event sinks
}
private async Task<CommunicationCall> JoinTeamsMeetingByLinkAsync(Uri teamsCallLink)
{
var joinCallOptions = GetJoinCallOptions();
var teamsMeetingLinkLocator = new TeamsMeetingLinkLocator(teamsCallLink.AbsoluteUri);
var call = await callAgent.JoinAsync(teamsMeetingLinkLocator, joinCallOptions);
return call;
}
private JoinCallOptions GetJoinCallOptions()
{
return new JoinCallOptions() {
OutgoingAudioOptions = new OutgoingAudioOptions() { IsMuted = true },
OutgoingVideoOptions = new OutgoingVideoOptions() { Streams = new OutgoingVideoStream[] { cameraStream } }
};
}
#endregion
}
}
Получение ссылки на собрание Teams
Ссылку на собрание Teams можно получить с помощью интерфейсов API Graph. Это действие подробно описано в документации по Graph.
Пакет SDK вызовов Служб коммуникации принимает полную ссылку на собрание Teams. Эта ссылка возвращается как часть ресурса onlineMeeting
, доступного под свойством joinWebUrl
. Вы также можете получить необходимую информацию о собрании по URL-адресу Присоединиться к собранию в самом приглашении на собрание Teams.
Запуск приложения и присоединение к собранию в Teams
Вы можете создать и запустить приложение в Visual Studio, выбрав Отладка>Начать отладку или нажав клавишу F5.
Вставьте контекст Teams в текстовое поле и нажмите Присоединиться к собранию Teams, чтобы присоединиться к собранию Teams из приложения Служб коммуникации Azure.
В этом кратком руководстве описывается, как присоединиться к конференции Teams с помощью пакета SDK для вызовов Служб коммуникации Azure для Android.
Пример кода
Итоговый код для этого краткого руководства можно найти на сайте GitHub.
Необходимые компоненты
Добавление элементов управления пользовательского интерфейса Teams
Замените код в activity_main.xml приведенным ниже фрагментом кода. Текстовое поле будет использоваться для ввода контекста собрания Teams, а кнопка — для присоединения к указанному собранию:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/teams_meeting_link"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Teams meeting link"
android:inputType="textUri"
android:layout_marginTop="100dp"
android:layout_marginHorizontal="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="70dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/join_meeting_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Join Meeting" />
<Button
android:id="@+id/hangup_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hangup" />
</LinearLayout>
<TextView
android:id="@+id/call_status_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/recording_status_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Включение элементов управления пользовательского интерфейса Teams
Замените содержимое файла MainActivity.java
приведенным ниже фрагментом:
package com.contoso.acsquickstart;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import com.azure.android.communication.calling.Call;
import com.azure.android.communication.calling.CallAgent;
import com.azure.android.communication.calling.CallClient;
import com.azure.android.communication.calling.HangUpOptions;
import com.azure.android.communication.calling.JoinCallOptions;
import com.azure.android.communication.common.CommunicationTokenCredential;
import com.azure.android.communication.calling.TeamsMeetingLinkLocator;
public class MainActivity extends AppCompatActivity {
private static final String[] allPermissions = new String[] { Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE };
private static final String UserToken = "<User_Access_Token>";
TextView callStatusBar;
TextView recordingStatusBar;
private CallAgent agent;
private Call call;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getAllPermissions();
createAgent();
Button joinMeetingButton = findViewById(R.id.join_meeting_button);
joinMeetingButton.setOnClickListener(l -> joinTeamsMeeting());
Button hangupButton = findViewById(R.id.hangup_button);
hangupButton.setOnClickListener(l -> leaveMeeting());
callStatusBar = findViewById(R.id.call_status_bar);
recordingStatusBar = findViewById(R.id.recording_status_bar);
}
/**
* Join Teams meeting
*/
private void joinTeamsMeeting() {
if (UserToken.startsWith("<")) {
Toast.makeText(this, "Please enter token in source code", Toast.LENGTH_SHORT).show();
return;
}
EditText calleeIdView = findViewById(R.id.teams_meeting_link);
String meetingLink = calleeIdView.getText().toString();
if (meetingLink.isEmpty()) {
Toast.makeText(this, "Please enter Teams meeting link", Toast.LENGTH_SHORT).show();
return;
}
JoinCallOptions options = new JoinCallOptions();
TeamsMeetingLinkLocator teamsMeetingLinkLocator = new TeamsMeetingLinkLocator(meetingLink);
call = agent.join(
getApplicationContext(),
teamsMeetingLinkLocator,
options);
call.addOnStateChangedListener(p -> setCallStatus(call.getState().toString()));
call.addOnIsRecordingActiveChangedListener(p -> setRecordingStatus(call.isRecordingActive()));
}
/**
* Leave from the meeting
*/
private void leaveMeeting() {
try {
call.hangUp(new HangUpOptions()).get();
} catch (ExecutionException | InterruptedException e) {
Toast.makeText(this, "Unable to leave meeting", Toast.LENGTH_SHORT).show();
}
}
/**
* Create the call agent
*/
private void createAgent() {
try {
CommunicationTokenCredential credential = new CommunicationTokenCredential(UserToken);
agent = new CallClient().createCallAgent(getApplicationContext(), credential).get();
} catch (Exception ex) {
Toast.makeText(getApplicationContext(), "Failed to create call agent.", Toast.LENGTH_SHORT).show();
}
}
/**
* Request each required permission if the app doesn't already have it.
*/
private void getAllPermissions() {
ArrayList<String> permissionsToAskFor = new ArrayList<>();
for (String permission : allPermissions) {
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
permissionsToAskFor.add(permission);
}
}
if (!permissionsToAskFor.isEmpty()) {
ActivityCompat.requestPermissions(this, permissionsToAskFor.toArray(new String[0]), 1);
}
}
/**
* Ensure all permissions were granted, otherwise inform the user permissions are missing.
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, int[] grantResults) {
boolean allPermissionsGranted = true;
for (int result : grantResults) {
allPermissionsGranted &= (result == PackageManager.PERMISSION_GRANTED);
}
if (!allPermissionsGranted) {
Toast.makeText(this, "All permissions are needed to make the call.", Toast.LENGTH_LONG).show();
finish();
}
}
/**
* Shows call status in status bar
*/
private void setCallStatus(String status) {
runOnUiThread(() -> callStatusBar.setText(status));
}
/**
* Shows recording status bar
*/
private void setRecordingStatus(boolean status) {
if (status == true) {
runOnUiThread(() -> recordingStatusBar.setText("This call is being recorded"));
}
else {
runOnUiThread(() -> recordingStatusBar.setText(""));
}
}
}
Получение ссылки на собрание Teams
Ссылку на собрание Teams можно получить с помощью интерфейсов API Graph. Это действие подробно описано в документации по Graph.
Пакет SDK вызовов Служб коммуникации принимает полную ссылку на собрание Teams. Эта ссылка возвращается как часть ресурса onlineMeeting
, доступного под свойством joinWebUrl
. Вы также можете получить необходимую информацию о собрании по URL-адресу Присоединиться к собранию в самом приглашении на собрание Teams.
Запуск приложения и присоединение к собранию в Teams
Теперь вы можете запустить приложение с помощью кнопки Run App (Запустить приложение) на панели инструментов или нажав клавиши SHIFT+F10. Вы увидите следующее:
Вставьте контекст Teams в текстовое поле и нажмите Присоединиться к собранию, чтобы присоединиться к конференции Teams из приложения Служб коммуникации Azure.
В этом кратком руководстве описывается, как присоединиться к конференции Teams с помощью пакета SDK для вызовов Служб коммуникации Azure для iOS.
Необходимые компоненты
- Рабочее приложение iOS, вызывающее Службы коммуникации.
- Развертывание Teams.
Для этого краткого руководства мы будем использовать бета-версию 12 пакета AzureCommunicationCalling SDK, поэтому нам нужно обновить подфайл и снова установить модули.
Добавьте следующий код в podfile и сохраните его (убедитесь, что "target" соответствует имени проекта).
platform :ios, '13.0'
use_frameworks!
target 'AzureCommunicationCallingSample' do
pod 'AzureCommunicationCalling', '1.0.0-beta.12'
end
Удалите папку Pods, Podfile.lock и файл .xcworkspace.
.
Запустите pod install
и откройте .xcworkspace
с помощью Xcode.
Добавление и включение элементов управления пользовательского интерфейса Teams
Замените код в ContentView.swift приведенным ниже фрагментом кода. Текстовое поле будет использоваться для ввода контекста собрания Teams, а кнопка — для присоединения к указанному собранию:
import SwiftUI
import AzureCommunicationCalling
import AVFoundation
struct ContentView: View {
@State var meetingLink: String = ""
@State var callStatus: String = ""
@State var message: String = ""
@State var recordingStatus: String = ""
@State var callClient: CallClient?
@State var callAgent: CallAgent?
@State var call: Call?
@State var callObserver: CallObserver?
var body: some View {
NavigationView {
Form {
Section {
TextField("Teams meeting link", text: $meetingLink)
Button(action: joinTeamsMeeting) {
Text("Join Teams Meeting")
}.disabled(callAgent == nil)
Button(action: leaveMeeting) {
Text("Leave Meeting")
}.disabled(call == nil)
Text(callStatus)
Text(message)
Text(recordingStatus)
}
}
.navigationBarTitle("Calling Quickstart")
}.onAppear {
// Initialize call agent
var userCredential: CommunicationTokenCredential?
do {
userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
print("ERROR: It was not possible to create user credential.")
self.message = "Please enter your token in source code"
return
}
self.callClient = CallClient()
// Creates the call agent
self.callClient?.createCallAgent(userCredential: userCredential!) { (agent, error) in
if error != nil {
self.message = "Failed to create CallAgent."
return
} else {
self.callAgent = agent
self.message = "Call agent successfully created."
}
}
}
}
func joinTeamsMeeting() {
// Ask permissions
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
let joinCallOptions = JoinCallOptions()
let teamsMeetingLinkLocator = TeamsMeetingLinkLocator(meetingLink: self.meetingLink)
self.callAgent?.join(with: teamsMeetingLinkLocator, joinCallOptions: joinCallOptions) {(call, error) in
if (error == nil) {
self.call = call
self.callObserver = CallObserver(self)
self.call!.delegate = self.callObserver
self.message = "Teams meeting joined successfully"
} else {
print("Failed to get call object")
return
}
}
}
}
}
func leaveMeeting() {
if let call = call {
call.hangUp(options: nil, completionHandler: { (error) in
if error == nil {
self.message = "Leaving Teams meeting was successful"
} else {
self.message = "Leaving Teams meeting failed"
}
})
} else {
self.message = "No active call to hangup"
}
}
}
class CallObserver : NSObject, CallDelegate {
private var owner:ContentView
init(_ view:ContentView) {
owner = view
}
public func call(_ call: Call, didChangeState args: PropertyChangedEventArgs) {
owner.callStatus = CallObserver.callStateToString(state: call.state)
if call.state == .disconnected {
owner.call = nil
owner.message = "Left Meeting"
} else if call.state == .inLobby {
owner.message = "Waiting in lobby !!"
} else if call.state == .connected {
owner.message = "Joined Meeting !!"
}
}
public func call(_ call: Call, didChangeRecordingState args: PropertyChangedEventArgs) {
if (call.isRecordingActive == true) {
owner.recordingStatus = "This call is being recorded"
}
else {
owner.recordingStatus = ""
}
}
private static func callStateToString(state: CallState) -> String {
switch state {
case .connected: return "Connected"
case .connecting: return "Connecting"
case .disconnected: return "Disconnected"
case .disconnecting: return "Disconnecting"
case .earlyMedia: return "EarlyMedia"
case .none: return "None"
case .ringing: return "Ringing"
case .inLobby: return "InLobby"
default: return "Unknown"
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Получение ссылки на собрание Teams
Ссылку на собрание Teams можно получить с помощью интерфейсов API Graph. Это действие подробно описано в документации по Graph.
Пакет SDK вызовов Служб коммуникации принимает полную ссылку на собрание Teams. Эта ссылка возвращается как часть ресурса onlineMeeting
, доступного под свойством joinWebUrl
. Вы также можете получить необходимую информацию о собрании по URL-адресу Присоединиться к собранию в самом приглашении на собрание Teams.
Запуск приложения и присоединение к собранию в Teams
Вы можете создать и запустить приложение в симуляторе iOS, выбрав "Запуск продукта>" или с помощью сочетания клавиш ('-R).
Вставьте контекст Teams в текстовое поле и нажмите Присоединиться к собранию Teams, чтобы присоединиться к собранию Teams из приложения Служб коммуникации Azure.
Очистка ресурсов
Если вы хотите отменить и удалить подписку на Службы коммуникации, можно удалить ресурс или группу ресурсов. При удалении группы ресурсов также удаляются все связанные с ней ресурсы. См. сведения об очистке ресурсов.
Следующие шаги
Дополнительные сведения см. в следующих статьях:
- Ознакомьтесь с нашим главным примером функции вызовов.
- Начало работы с библиотекой пользовательского интерфейса
- Узнайте больше о возможностях пакета SDK для вызовов
- Узнайте больше о принципе работы функции вызовов.