Início Rápido: juntar seu aplicativo de chamadas a uma fila de chamadas do Teams
Nesse início rápido, você aprenderá como iniciar uma chamada de um usuário dos Serviços de Comunicação do Azure para a Fila de Chamadas do Teams. Você conseguirá fazer isso através das seguintes etapas:
- Habilite a federação do recurso de Serviços de Comunicação do Azure com o Locatário do Teams.
- Selecione ou crie a fila de chamadas do Teams através do Centro de Administração do Teams.
- Obtenha o endereço de email da Fila de Chamadas através do Centro de Administração do Teams.
- Obtenha o ID do Objeto da Fila de Chamadas por meio da API do Graph.
- Iniciar uma chamada com o SDK de Chamada dos Serviços de Comunicação do Azure.
Para pular para o final, baixe este guia de início rápido como um exemplo no GitHub.
Habilitar a interoperabilidade em seu locatário do Teams
O usuário do Microsoft Entra com função de administrador do Teams pode executar o cmdlet do PowerShell com o módulo MicrosoftTeams para habilitar o recurso Serviços de Comunicação no locatário.
1. Preparar o módulo do Microsoft Teams
Primeiro, abra o PowerShell e valide a existência do módulo do Teams com o seguinte comando:
Get-module *teams*
Se você não vir o módulo MicrosoftTeams
, instale-o primeiro. Para instalar o módulo, você precisa executar o PowerShell como administrador. Em seguida, execute o seguinte comando:
Install-Module -Name MicrosoftTeams
Você será informado sobre os módulos que serão instalados, que podem ser confirmados com uma resposta Y
ou A
. Se o módulo estiver instalado, mas estiver desatualizado, você poderá executar o seguinte comando para atualizar o módulo:
Update-Module MicrosoftTeams
2. Conectar-se ao módulo do Microsoft Teams
Quando o módulo estiver instalado e pronto, você poderá se conectar ao módulo MicrosoftTeams com o seguinte comando. Você receberá uma solicitação com uma janela interativa para fazer logon. A conta de usuário que você usará precisa ter permissões de administrador do Teams. Caso contrário, você poderá obter uma resposta access denied
nas próximas etapas.
Connect-MicrosoftTeams
3. Habilitar a configuração do locatário
A interoperabilidade com recursos dos Serviços de Comunicação é controlada por meio da configuração do locatário e da política atribuída. O locatário do Teams tem uma única configuração de locatário e os usuários do Teams têm atribuída a política global ou política personalizada. Para obter mais informações, consulte Políticas de atribuição no Teams.
Após o logon bem-sucedido, você pode executar o cmdlet Set-CsTeamsAcsFederationConfiguration para habilitar o recurso dos Serviços de Comunicação em seu locatário. Substitua o texto IMMUTABLE_RESOURCE_ID
por uma ID de recurso imutável no recurso de comunicação. Você pode encontrar mais detalhes sobre como obter essas informações aqui.
$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist
4. Habilitar a política de locatário
Cada usuário do Teams atribuiu um External Access Policy
que determina se os usuários dos Serviços de Comunicação podem chamar esse usuário do Teams. Use o cmdlet Set-CsExternalAccessPolicy para garantir que a política atribuída ao usuário do Teams tenha EnableAcsFederationAccess
definido como $true
Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true
Criar ou selecionar Fila de Chamadas do Teams
A Fila de Chamadas do Teams é um recurso do Microsoft Teams que distribui com eficiência as chamadas recebidas entre um grupo de usuários ou agentes designados. É útil para cenários de atendimento ao cliente ou de call center. As chamadas são colocadas em uma fila e atribuídas ao próximo agente disponível com base em um método de roteamento predeterminado. Os agentes recebem notificações e podem lidar com as chamadas usando os controles de chamada do Teams. O recurso oferece relatórios e análises para o acompanhamento do desempenho. Ele simplifica o tratamento de chamadas, garante uma experiência consistente para o cliente e otimiza a produtividade dos agentes. Você pode selecionar a Fila de Chamadas existente ou criar uma nova Fila de Chamadas através do Centro de Administração do Teams.
Saiba mais sobre como criar uma fila de chamadas usando o Centro de Administração do Teams acessando aqui.
Encontrar a ID do objeto para a Fila de Chamadas
Depois que a fila de chamadas é criada, precisamos encontrar a ID de objeto correlacionada para usá-la posteriormente nas chamadas. A ID do objeto está conectada à conta de recursos que foi anexada à fila de chamadas, abra a guia Contas de Recursos no Administrador do Teams e encontre o email. Todas as informações necessárias para a Conta de Recursos podem ser encontradas no Microsoft Graph Explorer usando esse email na pesquisa.
https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com
Nos resultados, poderemos encontrar o campo "ID"
"userPrincipalName": "lab-test2-cq@contoso.com",
"id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"
Pré-requisitos
- Obtenha uma conta do Azure com uma assinatura ativa. Crie uma conta gratuitamente.
- Versões de LTS Ativo e de LTS de Manutenção do Node.js (8.11.1 e 10.14.1)
- Crie um recurso ativo dos Serviços de Comunicação. Crie um recurso dos Serviços de Comunicação.
Configurando
Criar um novo aplicativo do Node.js
Abra o terminal ou a janela de comando Criar um diretório para seu aplicativo e navegue até o diretório.
mkdir calling-quickstart && cd calling-quickstart
Instalar o pacote
Use o comando npm install
para instalar o SDK de Chamada dos Serviços de Comunicação do Azure para JavaScript.
Importante
Este guia de início rápido usa a versão next
do SDK da Chamada dos Serviços de Comunicação do Azure.
npm install @azure/communication-common@next --save
npm install @azure/communication-calling@next --save
Configurar o framework de aplicativos
Este guia de início rápido usa o webpack para agrupar os ativos do aplicativo. Execute o seguinte comando para instalar os pacotes npm webpack
, webpack-cli
e webpack-dev-server
e listá-los como dependências de desenvolvimento no package.json
:
npm install copy-webpack-plugin@^11.0.0 webpack@^5.88.2 webpack-cli@^5.1.4 webpack-dev-server@^4.15.1 --save-dev
Crie um arquivo index.html
no diretório raiz do projeto. Usaremos esse arquivo para configurar um layout básico que permitirá que o usuário faça uma chamada de vídeo 1:1.
O código é o seguinte:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Azure Communication Services - Calling Web SDK</title>
</head>
<body>
<h4>Azure Communication Services - Calling Web SDK</h4>
<input id="user-access-token"
type="text"
placeholder="User access token"
style="margin-bottom:1em; width: 500px;"/>
<button id="initialize-teams-call-agent" type="button">Initialize Call Agent</button>
<br>
<br>
<input id="application-object-id"
type="text"
placeholder="Enter callee's Teams user identity in format: 'APP_GUID'"
style="margin-bottom:1em; width: 500px; display: block;"/>
<button id="start-call-button" type="button" disabled="true">Start Call</button>
<button id="hangup-call-button" type="button" disabled="true">Hang up Call</button>
<button id="accept-call-button" type="button" disabled="true">Accept Call</button>
<button id="start-video-button" type="button" disabled="true">Start Video</button>
<button id="stop-video-button" type="button" disabled="true">Stop Video</button>
<br>
<br>
<div id="connectedLabel" style="color: #13bb13;" hidden>Call is connected!</div>
<br>
<div id="remoteVideoContainer" style="width: 40%;" hidden>Remote participants' video streams:</div>
<br>
<div id="localVideoContainer" style="width: 30%;" hidden>Local video stream:</div>
<!-- points to the bundle generated from client.js -->
<script src="./main.js"></script>
</body>
</html>
Modelo de objeto do SDK Web de chamada de Serviços de Comunicação do Azure
As seguintes classes e interfaces cuidam de alguns dos principais recursos do SDK de Chamada dos Serviços de Comunicação do Azure:
Nome | Descrição |
---|---|
CallClient |
O ponto de entrada principal para o SDK de Chamada. |
CallAgent |
Usado para iniciar e gerenciar chamadas. |
DeviceManager |
Usado para gerenciar dispositivos de mídia. |
Call |
Usado para representar uma Chamada. |
LocalVideoStream |
Usado para criar um fluxo de vídeo local para um dispositivo de câmera no sistema local. |
RemoteParticipant |
Usado para representar um participante remoto da Chamada. |
RemoteVideoStream |
Usado para representar um fluxo de vídeo remoto de um Participante Remoto. |
Crie um arquivo no diretório raiz do projeto chamado client.js
para conter a lógica do aplicativo deste guia de início rápido. Adicione o seguinte código ao client.js:
// Make sure to install the necessary dependencies
const { CallClient, VideoStreamRenderer, LocalVideoStream } = require('@azure/communication-calling');
const { AzureCommunicationTokenCredential } = require('@azure/communication-common');
const { AzureLogger, setLogLevel } = require("@azure/logger");
// Set the log level and output
setLogLevel('verbose');
AzureLogger.log = (...args) => {
console.log(...args);
};
// Calling web sdk objects
let callAgent;
let deviceManager;
let call;
let incomingCall;
let localVideoStream;
let localVideoStreamRenderer;
// UI widgets
let userAccessToken = document.getElementById('user-access-token');
let callQueueId = document.getElementById('application-object-id');
let initializeCallAgentButton = document.getElementById('initialize-teams-call-agent');
let startCallButton = document.getElementById('start-call-button');
let hangUpCallButton = document.getElementById('hangup-call-button');
let acceptCallButton = document.getElementById('accept-call-button');
let startVideoButton = document.getElementById('start-video-button');
let stopVideoButton = document.getElementById('stop-video-button');
let connectedLabel = document.getElementById('connectedLabel');
let remoteVideoContainer = document.getElementById('remoteVideoContainer');
let localVideoContainer = document.getElementById('localVideoContainer');
/**
* Create an instance of CallClient. Initialize a CallAgent instance with a AzureCommunicationTokenCredential via created CallClient. CallAgent enables us to make outgoing calls and receive incoming calls.
* You can then use the CallClient.getDeviceManager() API instance to get the DeviceManager.
*/
initializeCallAgentButton.onclick = async () => {
try {
const callClient = new CallClient();
tokenCredential = new AzureCommunicationTokenCredential(userAccessToken.value.trim());
callAgent = await callClient.createCallAgent(tokenCredential)
// Set up a camera device to use.
deviceManager = await callClient.getDeviceManager();
await deviceManager.askDevicePermission({ video: true });
await deviceManager.askDevicePermission({ audio: true });
// Listen for an incoming call to accept.
callAgent.on('incomingCall', async (args) => {
try {
incomingCall = args.incomingCall;
acceptCallButton.disabled = false;
startCallButton.disabled = true;
} catch (error) {
console.error(error);
}
});
startCallButton.disabled = false;
initializeCallAgentButton.disabled = true;
} catch(error) {
console.error(error);
}
}
/**
* Place a 1:1 outgoing video call to a Teams Call Queue
* Add an event listener to initiate a call when the `startCallButton` is selected.
* Enumerate local cameras using the deviceManager `getCameraList` API.
* In this quickstart, we're using the first camera in the collection. Once the desired camera is selected, a
* LocalVideoStream instance will be constructed and passed within `videoOptions` as an item within the
* localVideoStream array to the call method. When the call connects, your application will be sending a video stream to the other participant.
*/
startCallButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
call = callAgent.startCall([{ teamsAppId: callQueueId.value.trim(), cloud:"public" }], { videoOptions: videoOptions });
// Subscribe to the call's properties and events.
subscribeToCall(call);
} catch (error) {
console.error(error);
}
}
/**
* Accepting an incoming call with a video
* Add an event listener to accept a call when the `acceptCallButton` is selected.
* You can accept incoming calls after subscribing to the `CallAgent.on('incomingCall')` event.
* You can pass the local video stream to accept the call with the following code.
*/
acceptCallButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
call = await incomingCall.accept({ videoOptions });
// Subscribe to the call's properties and events.
subscribeToCall(call);
} catch (error) {
console.error(error);
}
}
// Subscribe to a call obj.
// Listen for property changes and collection updates.
subscribeToCall = (call) => {
try {
// Inspect the initial call.id value.
console.log(`Call Id: ${call.id}`);
//Subscribe to call's 'idChanged' event for value changes.
call.on('idChanged', () => {
console.log(`Call ID changed: ${call.id}`);
});
// Inspect the initial call.state value.
console.log(`Call state: ${call.state}`);
// Subscribe to call's 'stateChanged' event for value changes.
call.on('stateChanged', async () => {
console.log(`Call state changed: ${call.state}`);
if(call.state === 'Connected') {
connectedLabel.hidden = false;
acceptCallButton.disabled = true;
startCallButton.disabled = true;
hangUpCallButton.disabled = false;
startVideoButton.disabled = false;
stopVideoButton.disabled = false;
} else if (call.state === 'Disconnected') {
connectedLabel.hidden = true;
startCallButton.disabled = false;
hangUpCallButton.disabled = true;
startVideoButton.disabled = true;
stopVideoButton.disabled = true;
console.log(`Call ended, call end reason={code=${call.callEndReason.code}, subCode=${call.callEndReason.subCode}}`);
}
});
call.localVideoStreams.forEach(async (lvs) => {
localVideoStream = lvs;
await displayLocalVideoStream();
});
call.on('localVideoStreamsUpdated', e => {
e.added.forEach(async (lvs) => {
localVideoStream = lvs;
await displayLocalVideoStream();
});
e.removed.forEach(lvs => {
removeLocalVideoStream();
});
});
call.on('isLocalVideoStartedChanged', () => {
console.log(`isLocalVideoStarted changed: ${call.isLocalVideoStarted}`);
});
console.log(`isLocalVideoStarted: ${call.isLocalVideoStarted}`);
// Inspect the call's current remote participants and subscribe to them.
call.remoteParticipants.forEach(remoteParticipant => {
subscribeToRemoteParticipant(remoteParticipant);
});
// Subscribe to the call's 'remoteParticipantsUpdated' event to be
// notified when new participants are added to the call or removed from the call.
call.on('remoteParticipantsUpdated', e => {
// Subscribe to new remote participants that are added to the call.
e.added.forEach(remoteParticipant => {
subscribeToRemoteParticipant(remoteParticipant)
});
// Unsubscribe from participants that are removed from the call
e.removed.forEach(remoteParticipant => {
console.log('Remote participant removed from the call.');
});
});
} catch (error) {
console.error(error);
}
}
// Subscribe to a remote participant obj.
// Listen for property changes and collection updates.
subscribeToRemoteParticipant = (remoteParticipant) => {
try {
// Inspect the initial remoteParticipant.state value.
console.log(`Remote participant state: ${remoteParticipant.state}`);
// Subscribe to remoteParticipant's 'stateChanged' event for value changes.
remoteParticipant.on('stateChanged', () => {
console.log(`Remote participant state changed: ${remoteParticipant.state}`);
});
// Inspect the remoteParticipants's current videoStreams and subscribe to them.
remoteParticipant.videoStreams.forEach(remoteVideoStream => {
subscribeToRemoteVideoStream(remoteVideoStream)
});
// Subscribe to the remoteParticipant's 'videoStreamsUpdated' event to be
// notified when the remoteParticipant adds new videoStreams and removes video streams.
remoteParticipant.on('videoStreamsUpdated', e => {
// Subscribe to newly added remote participant's video streams.
e.added.forEach(remoteVideoStream => {
subscribeToRemoteVideoStream(remoteVideoStream)
});
// Unsubscribe from newly removed remote participants' video streams.
e.removed.forEach(remoteVideoStream => {
console.log('Remote participant video stream was removed.');
})
});
} catch (error) {
console.error(error);
}
}
/**
* Subscribe to a remote participant's remote video stream obj.
* You have to subscribe to the 'isAvailableChanged' event to render the remoteVideoStream. If the 'isAvailable' property
* changes to 'true' a remote participant is sending a stream. Whenever the availability of a remote stream changes
* you can choose to destroy the whole 'Renderer' a specific 'RendererView' or keep them. Displaying RendererView without a video stream will result in a blank video frame.
*/
subscribeToRemoteVideoStream = async (remoteVideoStream) => {
// Create a video stream renderer for the remote video stream.
let videoStreamRenderer = new VideoStreamRenderer(remoteVideoStream);
let view;
const renderVideo = async () => {
try {
// Create a renderer view for the remote video stream.
view = await videoStreamRenderer.createView();
// Attach the renderer view to the UI.
remoteVideoContainer.hidden = false;
remoteVideoContainer.appendChild(view.target);
} catch (e) {
console.warn(`Failed to createView, reason=${e.message}, code=${e.code}`);
}
}
remoteVideoStream.on('isAvailableChanged', async () => {
// Participant has switched video on.
if (remoteVideoStream.isAvailable) {
await renderVideo();
// Participant has switched video off.
} else {
if (view) {
view.dispose();
view = undefined;
}
}
});
// Participant has video on initially.
if (remoteVideoStream.isAvailable) {
await renderVideo();
}
}
// Start your local video stream.
// This will send your local video stream to remote participants so they can view it.
startVideoButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
await call.startVideo(localVideoStream);
} catch (error) {
console.error(error);
}
}
// Stop your local video stream.
// This will stop your local video stream from being sent to remote participants.
stopVideoButton.onclick = async () => {
try {
await call.stopVideo(localVideoStream);
} catch (error) {
console.error(error);
}
}
/**
* To render a LocalVideoStream, you need to create a new instance of VideoStreamRenderer, and then
* create a new VideoStreamRendererView instance using the asynchronous createView() method.
* You may then attach view.target to any UI element.
*/
// Create a local video stream for your camera device
createLocalVideoStream = async () => {
const camera = (await deviceManager.getCameras())[0];
if (camera) {
return new LocalVideoStream(camera);
} else {
console.error(`No camera device found on the system`);
}
}
// Display your local video stream preview in your UI
displayLocalVideoStream = async () => {
try {
localVideoStreamRenderer = new VideoStreamRenderer(localVideoStream);
const view = await localVideoStreamRenderer.createView();
localVideoContainer.hidden = false;
localVideoContainer.appendChild(view.target);
} catch (error) {
console.error(error);
}
}
// Remove your local video stream preview from your UI
removeLocalVideoStream = async() => {
try {
localVideoStreamRenderer.dispose();
localVideoContainer.hidden = true;
} catch (error) {
console.error(error);
}
}
// End the current call
hangUpCallButton.addEventListener("click", async () => {
// end the current call
await call.hangUp();
});
Adicionar o código do servidor local do webpack
Crie um arquivo no diretório-raiz do projeto chamado webpack.config.js para conter a lógica do servidor local deste início rápido. Adicione o seguinte código ao webpack.config.js:
const path = require('path');
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
mode: 'development',
entry: './client.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
static: {
directory: path.join(__dirname, './')
},
},
plugins: [
new CopyPlugin({
patterns: [
'./index.html'
]
}),
]
};
Executar o código
Use webpack-dev-server
para criar e executar o seu aplicativo. Execute o seguinte comando para empacotar o host de aplicativos em um servidor Web local:
npx webpack serve --config webpack.config.js
Etapas manuais para configurar a chamada:
- Abra o navegador e navegue até http://localhost:8080/.
- Digite um token de acesso do usuário válido. Confira a documentação do token de acesso do usuário se você ainda não tem tokens de acesso disponíveis para uso.
- Clique nos botões "Inicializar o Agente de Chamadas".
- Insira a ID do Objeto da fila de chamadas e selecione o botão "Iniciar chamada". O aplicativo iniciará a chamada de saída para a fila de chamadas com a ID de Objeto fornecida.a.
- A chamada está conectada à Fila de Chamadas.
- O usuário dos Serviços de Comunicação é roteado pela Fila de Chamadas com base em sua configuração.
Importante
Este recurso dos Serviços de Comunicação do Azure estão atualmente em versão prévia.
Versões prévias das APIs e dos SDKs são fornecidas sem um contrato de nível de serviço. É recomendável que você não as use para cargas de trabalho de produção. Alguns recursos podem não ter suporte ou talvez ter restrição de recursos.
Para saber mais, consulte os Termos de Uso Complementares das Versões Prévias do Microsoft Azure.
Neste início rápido, você aprenderá como iniciar uma chamada do usuário dos Serviços de Comunicação do Azure para a Fila de Chamadas do Teams. Você conseguirá isso com as seguintes etapas:
- Habilite a federação do recurso de Serviços de Comunicação do Azure com o Locatário do Teams.
- Selecione ou crie a fila de chamadas do Teams através do Centro de Administração do Teams.
- Obtenha o endereço de email da Fila de Chamadas através do Centro de Administração do Teams.
- Obtenha o ID do Objeto da Fila de Chamadas por meio da API do Graph.
- Iniciar uma chamada com o SDK de Chamada dos Serviços de Comunicação do Azure.
Para pular para o final, baixe este guia de início rápido como um exemplo no GitHub.
Habilitar a interoperabilidade em seu locatário do Teams
O usuário do Microsoft Entra com função de administrador do Teams pode executar o cmdlet do PowerShell com o módulo MicrosoftTeams para habilitar o recurso Serviços de Comunicação no locatário.
1. Preparar o módulo do Microsoft Teams
Primeiro, abra o PowerShell e valide a existência do módulo do Teams com o seguinte comando:
Get-module *teams*
Se você não vir o módulo MicrosoftTeams
, instale-o primeiro. Para instalar o módulo, você precisa executar o PowerShell como administrador. Em seguida, execute o seguinte comando:
Install-Module -Name MicrosoftTeams
Você será informado sobre os módulos que serão instalados, que podem ser confirmados com uma resposta Y
ou A
. Se o módulo estiver instalado, mas estiver desatualizado, você poderá executar o seguinte comando para atualizar o módulo:
Update-Module MicrosoftTeams
2. Conectar-se ao módulo do Microsoft Teams
Quando o módulo estiver instalado e pronto, você poderá se conectar ao módulo MicrosoftTeams com o seguinte comando. Você receberá uma solicitação com uma janela interativa para fazer logon. A conta de usuário que você usará precisa ter permissões de administrador do Teams. Caso contrário, você poderá obter uma resposta access denied
nas próximas etapas.
Connect-MicrosoftTeams
3. Habilitar a configuração do locatário
A interoperabilidade com recursos dos Serviços de Comunicação é controlada por meio da configuração do locatário e da política atribuída. O locatário do Teams tem uma única configuração de locatário e os usuários do Teams têm atribuída a política global ou política personalizada. Para obter mais informações, consulte Políticas de atribuição no Teams.
Após o logon bem-sucedido, você pode executar o cmdlet Set-CsTeamsAcsFederationConfiguration para habilitar o recurso dos Serviços de Comunicação em seu locatário. Substitua o texto IMMUTABLE_RESOURCE_ID
por uma ID de recurso imutável no recurso de comunicação. Você pode encontrar mais detalhes sobre como obter essas informações aqui.
$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist
4. Habilitar a política de locatário
Cada usuário do Teams atribuiu um External Access Policy
que determina se os usuários dos Serviços de Comunicação podem chamar esse usuário do Teams. Use o cmdlet Set-CsExternalAccessPolicy para garantir que a política atribuída ao usuário do Teams tenha EnableAcsFederationAccess
definido como $true
Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true
Criar ou selecionar Fila de Chamadas do Teams
A Fila de Chamadas do Teams é um recurso do Microsoft Teams que distribui com eficiência as chamadas recebidas entre um grupo de usuários ou agentes designados. É útil para cenários de atendimento ao cliente ou de call center. As chamadas são colocadas em uma fila e atribuídas ao próximo agente disponível com base em um método de roteamento predeterminado. Os agentes recebem notificações e podem lidar com as chamadas usando os controles de chamada do Teams. O recurso oferece relatórios e análises para o acompanhamento do desempenho. Ele simplifica o tratamento de chamadas, garante uma experiência consistente para o cliente e otimiza a produtividade dos agentes. Você pode selecionar a Fila de Chamadas existente ou criar uma nova Fila de Chamadas através do Centro de Administração do Teams.
Saiba mais sobre como criar uma fila de chamadas usando o Centro de Administração do Teams acessando aqui.
Encontrar a ID do objeto para a Fila de Chamadas
Depois que a fila de chamadas é criada, precisamos encontrar a ID de objeto correlacionada para usá-la posteriormente nas chamadas. O ID do objeto está conectado à conta de recurso que foi anexada à fila de chamadas - abra a guia Contas de recurso no administrador do Teams e encontre o email. Todas as informações necessárias para a Conta de Recursos podem ser encontradas no Microsoft Graph Explorer usando esse email na pesquisa.
https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com
Nos resultados, poderemos encontrar o campo "ID"
"userPrincipalName": "lab-test2-cq@contoso.com",
"id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"
Para usar no aplicativo de chamada, precisamos adicionar um prefixo a esse ID. Atualmente, os seguintes são suportados:
- Fila de chamadas de nuvem pública:
28:orgid:<id>
- Fila de chamadas de nuvem do governo:
28:gcch:<id>
Pré-requisitos
Uma conta do Azure com uma assinatura ativa. Crie uma conta gratuitamente.
Android Studio, para criar o seu aplicativo Android.
Um recurso dos Serviços de Comunicação implantado. Crie um recurso dos Serviços de Comunicação.
Um Token de Acesso do Usuário para o seu Serviço de Comunicação do Azure. Você também pode usar a CLI do Azure e executar o comando com sua cadeia de conexão para criar um usuário e um token de acesso.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Para obter detalhes, confira Usar a CLI do Azure para criar e gerenciar tokens de acesso.
Suporte mínimo para aplicativos de chamada do Teams: 2.12.0-beta.1
Configurando
Criar um aplicativo Android com uma atividade vazia
No Android Studio, selecione Iniciar um novo projeto do Android Studio.
Selecione o modelo de projeto "Atividade de Exibições Vazias" em "Telefone e Tablet".
Selecione o SDK Mínimo de "API 26: Android 8.0 (Oreo)" ou superior.
Instalar o pacote
Localize o projeto settings.gradle.kts
e certifique-se de que mavenCentral()
aparece na lista de repositórios em pluginManagement
e dependencyResolutionManagement
pluginManagement {
repositories {
...
mavenCentral()
...
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
...
mavenCentral()
}
}
Em seguida, no nível do módulo build.gradle, adicione as linhas a seguir às seções do Android e às dependências
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
...
implementation ("com.azure.android:azure-communication-calling:2.6.0")
...
}
Adicionar permissões ao manifesto do aplicativo
Para solicitar as permissões necessárias para fazer uma chamada, elas devem ser declaradas no Manifesto do Aplicativo (app/src/main/AndroidManifest.xml
). Substitua o conteúdo do arquivo pelo seguinte código:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.contoso.acsquickstart">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--Our Calling SDK depends on the Apache HTTP SDK.
When targeting Android SDK 28+, this library needs to be explicitly referenced.
See https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Configurar o layout do aplicativo
Duas entradas são necessárias: uma entrada de texto para a ID do receptor e um botão para realizar a chamada. Essas entradas podem ser adicionadas através do designer ou editando o layout xml. Crie um botão com uma ID de call_button
e uma entrada de texto de callee_id
. Navegue até (app/src/main/res/layout/activity_main.xml
) e substitua o conteúdo do arquivo pelo seguinte código:
<?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="${launchApp}">
<EditText
android:id="@+id/callee_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Callee Id"
android:inputType="textPersonName"
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="46dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/call_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Call" />
<Button
android:id="@+id/hangup_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hangup" />
</LinearLayout>
<TextView
android:id="@+id/status_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Criar as associações e o scaffolding da atividade principal
Com o layout criado, as associações podem ser adicionadas, bem como o scaffolding básico da atividade. A atividade administra a solicitação de permissões de runtime, criando o agente de chamada e fazendo a chamada quando o botão estiver pressionado. O método onCreate
foi substituído para invocar getAllPermissions
e createAgent
e adicionar as associações para o botão de chamada. Esse evento ocorre apenas uma vez quando a atividade é criada. Para saber mais, em onCreate
, confira o guia Noções básicas do ciclo de vida de atividade.
Navegue para MainActivity.java e substitua o conteúdo pelo seguinte código:
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.media.AudioManager;
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.common.CommunicationIdentifier;
import com.azure.android.communication.common.CommunicationUserIdentifier;
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.common.CommunicationTokenCredential;
import com.azure.android.communication.calling.StartCallOptions;
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 statusBar;
private CallAgent agent;
private Call call;
private Button callButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
callButton = findViewById(R.id.call_button);
getAllPermissions();
createAgent();
callButton.setOnClickListener(l -> startCall());
Button hangupButton = findViewById(R.id.hangup_button);
hangupButton.setOnClickListener(l -> endCall());
statusBar = findViewById(R.id.status_bar);
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
}
/**
* Start a call
*/
private void startCall() {
if (UserToken.startsWith("<")) {
Toast.makeText(this, "Please enter token in source code", Toast.LENGTH_SHORT).show();
return;
}
EditText calleeIdView = findViewById(R.id.callee_id);
String calleeId = calleeIdView.getText().toString();
if (calleeId.isEmpty()) {
Toast.makeText(this, "Please enter callee", Toast.LENGTH_SHORT).show();
return;
}
ArrayList<CommunicationIdentifier> participants = new ArrayList<>();
participants.add(new MicrosoftTeamsAppIdentifier(calleeId));
StartCallOptions options = new StartCallOptions();
call = agent.startCall(
getApplicationContext(),
participants,
options);
call.addOnStateChangedListener(p -> setStatus(call.getState().toString()));
}
/**
* Ends the call previously started
*/
private void endCall() {
try {
call.hangUp(new HangUpOptions()).get();
} catch (ExecutionException | InterruptedException e) {
Toast.makeText(this, "Unable to hang up call", 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();
}
}
/**
* 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 message in the status bar
*/
private void setStatus(String status) {
runOnUiThread(() -> statusBar.setText(status));
}
}
Solicitar permissões no runtime
Para o Android 6.0 e superior (nível da API 23) e targetSdkVersion
23 ou superior, as permissões são concedidas no runtime em vez de quando o aplicativo é instalado. Para suportá-lo, getAllPermissions
pode ser implementado para chamar ActivityCompat.checkSelfPermission
e ActivityCompat.requestPermissions
em cada permissão necessária.
/**
* 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);
}
}
Observação
Ao projetar o seu aplicativo, leve em consideração quando essas permissões devem ser solicitadas. As permissões devem ser solicitadas quando elas forem necessárias e não com antecedência. Para obter mais informações, confira o Guia de Permissões do Android.
Modelo de objeto
As seguintes classes e interfaces cuidam de alguns dos principais recursos do SDK de Chamada da Interface do Usuário dos Serviços de Comunicação do Azure:
Nome | Descrição |
---|---|
CallClient |
O CallClient é o ponto de entrada principal do SDK de Chamada. |
CallAgent |
O CallAgent é usado para iniciar e gerenciar chamadas. |
CommunicationTokenCredential |
O CommunicationTokenCredential é usado como credencial de token para instanciar o CallAgent . |
CommunicationIdentifier |
O CommunicationIdentifier é usado como um tipo diferente de participante que pode fazer parte de uma chamada. |
Criar um agente do token de acesso do usuário
Com um token de usuário, um agente de chamada autenticado pode criar uma instância. Geralmente, esse token é gerado a partir de um serviço com autenticação específica para o aplicativo. Para saber mais sobre tokens de acesso do usuário, confira o guia Tokens de acesso do usuário.
Para o guia de início rápido, substitua <User_Access_Token>
por um token de acesso de usuário gerado para o recurso do Serviço de Comunicação do Azure.
/**
* Create the call agent for placing calls
*/
private void createAgent() {
String userToken = "<User_Access_Token>";
try {
CommunicationTokenCredential credential = new CommunicationTokenCredential(userToken);
callAgent = new CallClient().createCallAgent(getApplicationContext(), credential).get();
} catch (Exception ex) {
Toast.makeText(getApplicationContext(), "Failed to create call agent.", Toast.LENGTH_SHORT).show();
}
}
Executar o código
O aplicativo agora pode ser iniciado usando o botão "Executar aplicativo" na barra de ferramentas.
Etapas manuais para configurar a chamada:
- Inicie o aplicativo usando o Android Studio.
- Insira o ID do objeto da fila de chamadas (com prefixo) e selecione o botão "Iniciar chamada". O aplicativo iniciará a chamada de saída para a Fila de Chamadas com o ID de objeto fornecido.
- A chamada está conectada à Fila de Chamadas.
- O usuário dos Serviços de Comunicação é roteado pela Fila de Chamadas com base em sua configuração.
Importante
Este recurso dos Serviços de Comunicação do Azure estão atualmente em versão prévia.
Versões prévias das APIs e dos SDKs são fornecidas sem um contrato de nível de serviço. É recomendável que você não as use para cargas de trabalho de produção. Alguns recursos podem não ter suporte ou talvez ter restrição de recursos.
Para saber mais, consulte os Termos de Uso Complementares das Versões Prévias do Microsoft Azure.
Neste início rápido, você aprenderá como iniciar uma chamada do usuário dos Serviços de Comunicação do Azure para a Fila de Chamadas do Teams. Você conseguirá isso com as seguintes etapas:
- Habilite a federação do recurso de Serviços de Comunicação do Azure com o Locatário do Teams.
- Selecione ou crie a fila de chamadas do Teams através do Centro de Administração do Teams.
- Obtenha o endereço de email da Fila de Chamadas através do Centro de Administração do Teams.
- Obtenha o ID do Objeto da Fila de Chamadas por meio da API do Graph.
- Iniciar uma chamada com o SDK de Chamada dos Serviços de Comunicação do Azure.
Para pular para o final, baixe este guia de início rápido como um exemplo no GitHub.
Habilitar a interoperabilidade em seu locatário do Teams
O usuário do Microsoft Entra com função de administrador do Teams pode executar o cmdlet do PowerShell com o módulo MicrosoftTeams para habilitar o recurso Serviços de Comunicação no locatário.
1. Preparar o módulo do Microsoft Teams
Primeiro, abra o PowerShell e valide a existência do módulo do Teams com o seguinte comando:
Get-module *teams*
Se você não vir o módulo MicrosoftTeams
, instale-o primeiro. Para instalar o módulo, você precisa executar o PowerShell como administrador. Em seguida, execute o seguinte comando:
Install-Module -Name MicrosoftTeams
Você será informado sobre os módulos que serão instalados, que podem ser confirmados com uma resposta Y
ou A
. Se o módulo estiver instalado, mas estiver desatualizado, você poderá executar o seguinte comando para atualizar o módulo:
Update-Module MicrosoftTeams
2. Conectar-se ao módulo do Microsoft Teams
Quando o módulo estiver instalado e pronto, você poderá se conectar ao módulo MicrosoftTeams com o seguinte comando. Você receberá uma solicitação com uma janela interativa para fazer logon. A conta de usuário que você usará precisa ter permissões de administrador do Teams. Caso contrário, você poderá obter uma resposta access denied
nas próximas etapas.
Connect-MicrosoftTeams
3. Habilitar a configuração do locatário
A interoperabilidade com recursos dos Serviços de Comunicação é controlada por meio da configuração do locatário e da política atribuída. O locatário do Teams tem uma única configuração de locatário e os usuários do Teams têm atribuída a política global ou política personalizada. Para obter mais informações, consulte Políticas de atribuição no Teams.
Após o logon bem-sucedido, você pode executar o cmdlet Set-CsTeamsAcsFederationConfiguration para habilitar o recurso dos Serviços de Comunicação em seu locatário. Substitua o texto IMMUTABLE_RESOURCE_ID
por uma ID de recurso imutável no recurso de comunicação. Você pode encontrar mais detalhes sobre como obter essas informações aqui.
$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist
4. Habilitar a política de locatário
Cada usuário do Teams atribuiu um External Access Policy
que determina se os usuários dos Serviços de Comunicação podem chamar esse usuário do Teams. Use o cmdlet Set-CsExternalAccessPolicy para garantir que a política atribuída ao usuário do Teams tenha EnableAcsFederationAccess
definido como $true
Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true
Criar ou selecionar Fila de Chamadas do Teams
A Fila de Chamadas do Teams é um recurso do Microsoft Teams que distribui com eficiência as chamadas recebidas entre um grupo de usuários ou agentes designados. É útil para cenários de atendimento ao cliente ou de call center. As chamadas são colocadas em uma fila e atribuídas ao próximo agente disponível com base em um método de roteamento predeterminado. Os agentes recebem notificações e podem lidar com as chamadas usando os controles de chamada do Teams. O recurso oferece relatórios e análises para o acompanhamento do desempenho. Ele simplifica o tratamento de chamadas, garante uma experiência consistente para o cliente e otimiza a produtividade dos agentes. Você pode selecionar a Fila de Chamadas existente ou criar uma nova Fila de Chamadas através do Centro de Administração do Teams.
Saiba mais sobre como criar uma fila de chamadas usando o Centro de Administração do Teams acessando aqui.
Encontrar a ID do objeto para a Fila de Chamadas
Depois que a fila de chamadas é criada, precisamos encontrar a ID de objeto correlacionada para usá-la posteriormente nas chamadas. O ID do objeto está conectado à conta de recurso que foi anexada à fila de chamadas - abra a guia Contas de recurso no administrador do Teams e encontre o email. Todas as informações necessárias para a Conta de Recursos podem ser encontradas no Microsoft Graph Explorer usando esse email na pesquisa.
https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com
Nos resultados, poderemos encontrar o campo "ID"
"userPrincipalName": "lab-test2-cq@contoso.com",
"id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"
Para usar no aplicativo de chamada, precisamos adicionar um prefixo a esse ID. Atualmente, os seguintes são suportados:
- Fila de chamadas de nuvem pública:
28:orgid:<id>
- Fila de chamadas de nuvem do governo:
28:gcch:<id>
Pré-requisitos
Obtenha uma conta do Azure com uma assinatura ativa. Crie uma conta gratuitamente.
Um Mac executando Xcode, juntamente com um certificado de desenvolvedor válido instalado em seu conjunto de chaves.
Um recurso dos Serviços de Comunicação implantado. Crie um recurso dos Serviços de Comunicação. Você precisa gravar sua cadeia de conexão nesse início rápido.
Um Token de Acesso do Usuário para o seu Serviço de Comunicação do Azure. Você também pode usar a CLI do Azure e executar o comando com sua cadeia de conexão para criar um usuário e um token de acesso.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Para obter detalhes, confira Usar a CLI do Azure para criar e gerenciar tokens de acesso.
Suporte mínimo para aplicativos de chamada do Teams: 2.14.0-beta.1
Configurando
Como criar o projeto do Xcode
No Xcode, crie um projeto do iOS e selecione o modelo Aplicativo. Este tutorial usa a estrutura SwiftUI, portanto, você deve definir a Linguagem como Swift e a Interface do Usuário como SwiftUI. Você não criará testes durante este guia de início rápido. Fique à vontade para desmarcar Incluir Testes.
Instale o pacote e as dependências com o CocoaPods
Para criar um Podfile para seu aplicativo, abra o terminal e navegue até a pasta do projeto e execute:
pod init
Adicione o seguinte código ao Podfile e salve (verifique se "target" corresponde ao nome do seu projeto):
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 2.14.0-beta.1' end
Execute
pod install
.Abra o
.xcworkspace
com o Xcode.
Solicitar acesso ao microfone
Para acessar o microfone do dispositivo, você precisa atualizar a Lista de Propriedades de Informações do aplicativo com um NSMicrophoneUsageDescription
. Você define o valor associado como string
que foi incluído na caixa de diálogo que o sistema usa para solicitar o acesso do usuário.
Clique com o botão direito do mouse na entrada Info.plist
da árvore do projeto e selecione Abrir Como>Código-Fonte. Adicione as linhas a seguir no nível superior da seção <dict>
e, em seguida, salve o arquivo.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
Configurar o framework de aplicativos
Abra o arquivo ContentView.swift do seu projeto e adicione uma declaração import
à parte superior do arquivo para importar o AzureCommunicationCalling library
. Além disso, importe AVFoundation
, precisamos desse código para a solicitação da permissão de áudio no código.
import AzureCommunicationCalling
import AVFoundation
Substitua a implementação do struct ContentView
por alguns controles de interface do usuário simples que permitem que um usuário inicie e termine uma chamada. Anexamos a lógica de negócios a esses controles neste início rápido.
struct ContentView: View {
@State var callee: String = ""
@State var callClient: CallClient?
@State var callAgent: CallAgent?
@State var call: Call?
var body: some View {
NavigationView {
Form {
Section {
TextField("Who would you like to call?", text: $callee)
Button(action: startCall) {
Text("Start Call")
}.disabled(callAgent == nil)
Button(action: endCall) {
Text("End Call")
}.disabled(call == nil)
}
}
.navigationBarTitle("Calling Quickstart")
}.onAppear {
// Initialize call agent
}
}
func startCall() {
// Ask permissions
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
// Add start call logic
}
}
}
func endCall() {
// Add end call logic
}
}
Modelo de objeto
As seguintes classes e interfaces cuidam de alguns dos principais recursos do SDK de Chamada da Interface do Usuário dos Serviços de Comunicação do Azure:
Nome | Descrição |
---|---|
CallClient |
O CallClient é o ponto de entrada principal do SDK de Chamada. |
CallAgent |
O CallAgent é usado para iniciar e gerenciar chamadas. |
CommunicationTokenCredential |
O CommunicationTokenCredential é usado como credencial de token para instanciar o CallAgent . |
CommunicationUserIdentifier |
O CommunicationUserIdentifier é usado para representar a identidade do usuário, que pode ser uma das seguintes opções: CommunicationUserIdentifier ,PhoneNumberIdentifier ou CallingApplication. |
Autenticar o cliente
Inicialize uma instância CallAgent
com um Token de Acesso do Usuário, o que nos permite fazer e receber chamadas.
No código a seguir, você precisa substituir <USER ACCESS TOKEN>
por um token de acesso do usuário válido para seu recurso. Confira a documentação do token de acesso do usuário se você ainda não tiver um token disponível.
Adicione o seguinte código ao retorno de chamada onAppear
no ContentView.swift:
var userCredential: CommunicationTokenCredential?
do {
userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
print("ERROR: It was not possible to create user credential.")
return
}
self.callClient = CallClient()
// Creates the call agent
self.callClient?.createCallAgent(userCredential: userCredential!) { (agent, error) in
if error != nil {
print("ERROR: It was not possible to create a call agent.")
return
}
else {
self.callAgent = agent
print("Call agent successfully created.")
}
}
Iniciar uma chamada
O método startCall
é definido como a ação que é realizada quando o botão Iniciar Chamada é tocado. Atualize a implementação para iniciar uma chamada com o ASACallAgent
:
func startCall()
{
// Ask permissions
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
// start call logic
let callees:[CommunicationIdentifier] = [MicrosoftTeamsAppIdentifier(self.callee)]
self.callAgent?.startCall(participants: callees, options: StartCallOptions()) { (call, error) in
if (error == nil) {
self.call = call
} else {
print("Failed to get call object")
}
}
}
}
}
Você também pode usar as propriedades em StartCallOptions
para definir as opções iniciais da chamada (ou seja, ela permite iniciar a chamada com o microfone silenciado).
Encerrar uma chamada
Implemente o método endCall
para encerrar a chamada atual quando o botão Encerrar Chamada for tocado.
func endCall()
{
self.call!.hangUp(options: HangUpOptions()) { (error) in
if (error != nil) {
print("ERROR: It was not possible to hangup the call.")
}
}
}
Executar o código
Você pode compilar e executar o aplicativo no simulador de iOS selecionando Produto>Executar ou usando o atalho de teclado (⌘-R).
Observação
Na primeira vez que você fizer uma chamada, o sistema solicitará o acesso ao microfone. Em um aplicativo de produção, você deve usar a API AVAudioSession
para verificar o status de permissão e atualizar normalmente o comportamento do aplicativo quando a permissão não for concedida.
Etapas manuais para configurar a chamada:
- Inicie o aplicativo usando o Xcode
- Insira o ID do objeto da fila de chamadas (com prefixo) e selecione o botão "Iniciar chamada". O aplicativo iniciará a chamada de saída para a Fila de Chamadas com o ID de objeto fornecido.
- A chamada está conectada à Fila de Chamadas.
- O usuário dos Serviços de Comunicação é roteado pela Fila de Chamadas com base em sua configuração.
Importante
Este recurso dos Serviços de Comunicação do Azure estão atualmente em versão prévia.
Versões prévias das APIs e dos SDKs são fornecidas sem um contrato de nível de serviço. É recomendável que você não as use para cargas de trabalho de produção. Alguns recursos podem não ter suporte ou talvez ter restrição de recursos.
Para saber mais, consulte os Termos de Uso Complementares das Versões Prévias do Microsoft Azure.
Neste início rápido, você aprenderá como iniciar uma chamada do usuário dos Serviços de Comunicação do Azure para a Fila de Chamadas do Teams. Você conseguirá isso com as seguintes etapas:
- Habilite a federação do recurso de Serviços de Comunicação do Azure com o Locatário do Teams.
- Selecione ou crie a fila de chamadas do Teams através do Centro de Administração do Teams.
- Obtenha o endereço de email da Fila de Chamadas através do Centro de Administração do Teams.
- Obtenha o ID do Objeto da Fila de Chamadas por meio da API do Graph.
- Iniciar uma chamada com o SDK de Chamada dos Serviços de Comunicação do Azure.
Para pular para o final, baixe este guia de início rápido como um exemplo no GitHub.
Habilitar a interoperabilidade em seu locatário do Teams
O usuário do Microsoft Entra com função de administrador do Teams pode executar o cmdlet do PowerShell com o módulo MicrosoftTeams para habilitar o recurso Serviços de Comunicação no locatário.
1. Preparar o módulo do Microsoft Teams
Primeiro, abra o PowerShell e valide a existência do módulo do Teams com o seguinte comando:
Get-module *teams*
Se você não vir o módulo MicrosoftTeams
, instale-o primeiro. Para instalar o módulo, você precisa executar o PowerShell como administrador. Em seguida, execute o seguinte comando:
Install-Module -Name MicrosoftTeams
Você será informado sobre os módulos que serão instalados, que podem ser confirmados com uma resposta Y
ou A
. Se o módulo estiver instalado, mas estiver desatualizado, você poderá executar o seguinte comando para atualizar o módulo:
Update-Module MicrosoftTeams
2. Conectar-se ao módulo do Microsoft Teams
Quando o módulo estiver instalado e pronto, você poderá se conectar ao módulo MicrosoftTeams com o seguinte comando. Você receberá uma solicitação com uma janela interativa para fazer logon. A conta de usuário que você usará precisa ter permissões de administrador do Teams. Caso contrário, você poderá obter uma resposta access denied
nas próximas etapas.
Connect-MicrosoftTeams
3. Habilitar a configuração do locatário
A interoperabilidade com recursos dos Serviços de Comunicação é controlada por meio da configuração do locatário e da política atribuída. O locatário do Teams tem uma única configuração de locatário e os usuários do Teams têm atribuída a política global ou política personalizada. Para obter mais informações, consulte Políticas de atribuição no Teams.
Após o logon bem-sucedido, você pode executar o cmdlet Set-CsTeamsAcsFederationConfiguration para habilitar o recurso dos Serviços de Comunicação em seu locatário. Substitua o texto IMMUTABLE_RESOURCE_ID
por uma ID de recurso imutável no recurso de comunicação. Você pode encontrar mais detalhes sobre como obter essas informações aqui.
$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist
4. Habilitar a política de locatário
Cada usuário do Teams atribuiu um External Access Policy
que determina se os usuários dos Serviços de Comunicação podem chamar esse usuário do Teams. Use o cmdlet Set-CsExternalAccessPolicy para garantir que a política atribuída ao usuário do Teams tenha EnableAcsFederationAccess
definido como $true
Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true
Criar ou selecionar Fila de Chamadas do Teams
A Fila de Chamadas do Teams é um recurso do Microsoft Teams que distribui com eficiência as chamadas recebidas entre um grupo de usuários ou agentes designados. É útil para cenários de atendimento ao cliente ou de call center. As chamadas são colocadas em uma fila e atribuídas ao próximo agente disponível com base em um método de roteamento predeterminado. Os agentes recebem notificações e podem lidar com as chamadas usando os controles de chamada do Teams. O recurso oferece relatórios e análises para o acompanhamento do desempenho. Ele simplifica o tratamento de chamadas, garante uma experiência consistente para o cliente e otimiza a produtividade dos agentes. Você pode selecionar a Fila de Chamadas existente ou criar uma nova Fila de Chamadas através do Centro de Administração do Teams.
Saiba mais sobre como criar uma fila de chamadas usando o Centro de Administração do Teams acessando aqui.
Encontrar a ID do objeto para a Fila de Chamadas
Depois que a fila de chamadas é criada, precisamos encontrar a ID de objeto correlacionada para usá-la posteriormente nas chamadas. O ID do objeto está conectado à conta de recurso que foi anexada à fila de chamadas - abra a guia Contas de recurso no administrador do Teams e encontre o email. Todas as informações necessárias para a Conta de Recursos podem ser encontradas no Microsoft Graph Explorer usando esse email na pesquisa.
https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com
Nos resultados, poderemos encontrar o campo "ID"
"userPrincipalName": "lab-test2-cq@contoso.com",
"id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"
Para usar no aplicativo de chamada, precisamos adicionar um prefixo a esse ID. Atualmente, os seguintes são suportados:
- Fila de chamadas de nuvem pública:
28:orgid:<id>
- Fila de chamadas de nuvem do governo:
28:gcch:<id>
Pré-requisitos
Para concluir este tutorial, você precisará dos seguintes pré-requisitos:
Uma conta do Azure com uma assinatura ativa. Crie uma conta gratuitamente.
Instale o Visual Studio 2022 com a carga de trabalho de desenvolvimento da Plataforma Universal do Windows.
Um recurso dos Serviços de Comunicação implantado. Crie um recurso dos Serviços de Comunicação. Você precisa gravar sua cadeia de conexão nesse início rápido.
Um Token de Acesso do Usuário para o seu Serviço de Comunicação do Azure. Você também pode usar a CLI do Azure e executar o comando com sua cadeia de conexão para criar um usuário e um token de acesso.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Para obter detalhes, confira Usar a CLI do Azure para criar e gerenciar tokens de acesso.
Suporte mínimo para aplicativos de chamada do Teams: 1.10.0-beta.1
Configurando
Criação do projeto
No Visual Studio, crie um novo projeto com o modelo Aplicativo em branco (Universal do Windows) para configurar um aplicativo UWP (Plataforma Universal do Windows de página única).
Instalar o pacote
Selecione seu projeto com o botão direito e vá para Manage Nuget Packages
para instalar Azure.Communication.Calling.WindowsClient
1.4.0 ou superior. Verifique se Include Prerelease
foi verificado se você deseja ver as versões para visualização pública.
Solicitar acesso
Vá para Package.appxmanifest
e selecione Capabilities
.
Verifique Internet (Client)
e Internet (Client & Server)
para obter acesso de entrada e saída à internet. Verifique Microphone
para acessar o feed de áudio do microfone e Webcam
para acessar o feed de vídeo da câmera.
Configurar o framework de aplicativos
Precisamos configurar um layout básico para anexar nossa lógica. Para fazer uma chamada de saída, precisamos de um TextBox
para fornecer a ID de usuário do receptor. Também precisamos de um botão Start/Join call
e de um botão Hang up
. Uma caixa de seleção Mute
e BackgroundBlur
também são incluídas neste exemplo para demonstrar os recursos de estados de áudio e efeitos de vídeo.
Abra o MainPage.xaml
do seu projeto e adicione o nó Grid
ao seu Page
:
<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">
<!-- Don't forget to replace ‘CallingQuickstart’ with your project’s name -->
<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">
<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"/>
<CheckBox x:Name="MuteLocal" Content="Mute" Margin="10,0,0,0" Click="MuteLocal_Click" Width="74"/>
</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>
Abra o MainPage.xaml.cs
e substitua o conteúdo pela seguinte implementação:
using Azure.Communication.Calling.WindowsClient;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Core;
using Windows.Media.Core;
using Windows.Networking.PushNotifications;
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;
#region Page initialization
public MainPage()
{
this.InitializeComponent();
// Additional UI customization code goes here
}
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)
{
// Start a call
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
// Hang up a call
}
private async void MuteLocal_Click(object sender, RoutedEventArgs e)
{
// Toggle mute/unmute audio state of a call
}
#endregion
#region API event handlers
private async void OnIncomingCallAsync(object sender, IncomingCallReceivedEventArgs args)
{
// Handle incoming call event
}
private async void OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
{
// Handle connected and disconnected state change of a call
}
#endregion
#region Helper methods
private async Task InitCallAgentAndDeviceManagerAsync()
{
//Initialize the call agent and search for devices
}
private async Task<CommunicationCall> StartCallAsync(string acsCallee)
{
// Start a call to an Azure Communication Services user using the CallAgent and the callee id
}
#endregion
}
}
Modelo de objeto
A tabela a seguir lista as classes e interfaces que lidam com alguns dos principais recursos do SDK de Chamadas dos Serviços de Comunicação do Azure:
Nome | Descrição |
---|---|
CallClient |
O CallClient é o ponto de entrada principal do SDK de Chamada. |
CallAgent |
O CallAgent é usado para iniciar e gerenciar chamadas. |
CommunicationCall |
O CommunicationCall é usado para gerenciar uma chamada em andamento. |
CallTokenCredential |
O CallTokenCredential é usado como a credencial de token de segurança para criar uma instância CallAgent . |
CallIdentifier |
O CallIdentifier é usado para representar a identidade do usuário, que pode ser uma das seguintes opções: UserCallIdentifier , PhoneNumberCallIdentifier etc. |
Autenticar o cliente
Inicializar uma instância CallAgent
com um Token de Acesso do Usuário que nos permite fazer e receber chamadas e, opcionalmente, obter uma instância DeviceManager para consultar as configurações do dispositivo cliente.
No código, substitua <AUTHENTICATION_TOKEN>
por um Token de Acesso de Usuário. Confira a documentação do token de acesso do usuário se você ainda não tiver um token disponível.
Adicione a função InitCallAgentAndDeviceManagerAsync
, que inicializa o SDK. Esse auxiliar pode ser personalizado para atender aos requisitos do seu aplicativo.
private async Task InitCallAgentAndDeviceManagerAsync()
{
this.callClient = new CallClient(new CallClientOptions() {
Diagnostics = new CallDiagnosticsOptions() {
// make sure to put your project AppName
AppName = "CallingQuickstart",
AppVersion="1.0",
Tags = new[] { "Calling", "ACS", "Windows" }
}
});
// Set up local audio stream using the first mic enumerated
var deviceManager = await this.callClient.GetDeviceManagerAsync();
var mic = deviceManager?.Microphones?.FirstOrDefault();
micStream = new LocalOutgoingAudioStream();
var tokenCredential = new CallTokenCredential(authToken, callTokenRefreshOptions);
var callAgentOptions = new CallAgentOptions()
{
DisplayName = $"{Environment.MachineName}/{Environment.UserName}",
};
this.callAgent = await this.callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
this.callAgent.IncomingCallReceived += OnIncomingCallAsync;
}
Inicie a chamada
Uma vez que um objeto StartCallOptions
for obtido, CallAgent
pode ser usado para iniciar a chamada de Serviços de Comunicação do Azure:
private async Task<CommunicationCall> StartCallAsync(string acsCallee)
{
var options = new StartCallOptions();
var call = await this.callAgent.StartCallAsync( new [] { new MicrosoftTeamsAppCallIdentifier(acsCallee) }, options);
return call;
}
Encerrar uma chamada
Finalize a chamada atual quando o botão Hang up
for clicado. Adicione a implementação ao HangupButton_Click para encerrar uma chamada e interromper a visualização e os fluxos de vídeo.
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
await call.HangUpAsync(new HangUpOptions() { ForEveryone = false });
}
}
Ativar/desativar mudo no áudio
Ative o mudo do áudio de saída quando o botão Mute
for clicado. Adicione a implementação ao MuteLocal_Click para ativar o mudo da chamada.
private async void MuteLocal_Click(object sender, RoutedEventArgs e)
{
var muteCheckbox = sender as CheckBox;
if (muteCheckbox != null)
{
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
if ((bool)muteCheckbox.IsChecked)
{
await call.MuteOutgoingAudioAsync();
}
else
{
await call.UnmuteOutgoingAudioAsync();
}
}
// Update the UI to reflect the state
}
}
Aceitar uma chamada de entrada
O coletor de eventos IncomingCallReceived
é configurado no auxiliar de inicialização do SDK InitCallAgentAndDeviceManagerAsync
.
this.callAgent.IncomingCallReceived += OnIncomingCallAsync;
O aplicativo tem a oportunidade de configurar como a chamada de entrada deve ser aceita, como tipos de fluxo de áudio e vídeo.
private async void OnIncomingCallAsync(object sender, IncomingCallReceivedEventArgs args)
{
var incomingCall = args.IncomingCall;
var acceptCallOptions = new AcceptCallOptions() { };
call = await incomingCall.AcceptAsync(acceptCallOptions);
call.StateChanged += OnStateChangedAsync;
}
Monitorar e responder ao evento de alteração de estado de chamada
O evento StateChanged
no objeto CommunicationCall
é disparado quando uma transação em andamento chama de um estado para outro. O aplicativo tem as oportunidades de refletir as alterações de estado na interface do usuário ou inserir lógicas de negócios.
private async void OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
{
var call = sender as CommunicationCall;
if (call != null)
{
var state = call.State;
// Update the UI
switch (state)
{
case CallState.Connected:
{
await call.StartAudioAsync(micStream);
break;
}
case CallState.Disconnected:
{
call.StateChanged -= OnStateChangedAsync;
call.Dispose();
break;
}
default: break;
}
}
}
Fazer o botão de chamada funcionar
Quando o valor Callee ID
não for nulo ou vazio, você poderá iniciar uma chamada.
O estado da chamada deve ser alterado usando a ação OnStateChangedAsync
.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
if (!string.IsNullOrEmpty(callString))
{
call = await StartCallAsync(callString);
call.StateChanged += OnStateChangedAsync;
}
}
Executar o código
Você pode criar e executar o código no Visual Studio. Para plataformas de solução, oferecemos suporte a ARM64
, x64
e x86
.
Etapas manuais para configurar a chamada:
- Inicie o aplicativo usando o Visual Studio.
- Insira o ID do objeto da fila de chamadas (com prefixo) e selecione o botão "Iniciar chamada". O aplicativo iniciará a chamada de saída para a Fila de Chamadas com o ID de objeto fornecido.
- A chamada está conectada à Fila de Chamadas.
- O usuário dos Serviços de Comunicação é roteado pela Fila de Chamadas com base em sua configuração.
Limpar os recursos
Se quiser limpar e remover uma assinatura dos Serviços de Comunicação, exclua o recurso ou o grupo de recursos. Excluir o grupo de recursos também exclui todos os recursos associados a ele. Saiba mais sobre como limpar recursos.
Próximas etapas
Para obter mais informações, consulte os seguintes artigos:
- Introdução à chamada de interface do usuário para aplicativos de voz do Teams
- Conheça os recursos do SDK de Chamada
- Saiba mais sobre como a chamada funciona