Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo illustra come integrare le funzionalità di chiamata e chat nell'app Android o iOS usando la libreria dell'interfaccia utente di Servizi di comunicazione di Azure.
Prerequisiti
- Un account Azure e una sottoscrizione di Azure attiva. Creare un account gratuitamente.
- Un sistema operativo che esegue Android Studio.
- Una risorsa distribuita di Servizi di comunicazione di Azure.
- Token di accesso di Servizi di comunicazione di Azure.
Ottenere un esempio completo
È possibile ottenere un progetto di esempio completo da GitHub.
Configurare il progetto
In Android Studio, creare un nuovo progetto:
Scegliere Nuovo>progetto dal menu File.
In Nuovo progetto, selezionare il modello di progetto Attività vuota.
Selezionare Avanti.
In Attività vuota, assegnare al progetto il nome UILibraryQuickStart. Per il linguaggio selezionare Java o Kotlin. Per l'SDK minimo, scegliere API 26: Android 8.0 (Oreo) o versione successiva.
Selezionare Fine.
Installare i pacchetti
Per installare i pacchetti dell'applicazione necessari, completare le sezioni seguenti.
Aggiungere una dipendenza
Nel file UILibraryQuickStart/app/build.gradle a livello di app aggiungere la dipendenza seguente:
dependencies {
...
implementation("com.azure.android:azure-communication-ui-calling:+")
implementation("com.azure.android:azure-communication-ui-chat:+")
...
}
Aggiungere un'esclusione META-INF
alla sezione UILibraryQuickStart/app/build.gradleandroid
:
packaging {
resources.excludes.add("META-INF/*")
}
Aggiungere archivi Maven
Per integrare la libreria sono necessari due repository Maven:
Repository
mavenCentral
Repository di pacchetti di Azure
repositories { ... mavenCentral() maven { url = URI("https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1") } ... }
Connettersi alla riunione di Teams con chiamate e chat
CallComposite
Usare per connettersi alla chiamata. Dopo che un utente è stato ammesso alla chiamata, CallComposite
notifica l'utente modificando lo stato su connected
. L'utente può quindi essere connesso al thread di chat.
Quando l'utente seleziona il pulsante Chat , viene aggiunto un pulsante personalizzato a CallComposite
. CallComposite
è ridotta a icona e la chat viene visualizzata in Teams.
Aggiungere un pulsante e una visualizzazione contenitore di chat per Activity_main.xml
Nel file di layout app/src/main/res/layout/activity_main.xml, aggiungere il codice seguente per creare un pulsante per l'avvio del composito:
<?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">
<LinearLayout
android:id="@+id/buttonContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
>
<Button
android:id="@+id/startCallButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Call"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="4dp"
/>
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/chatContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@+id/buttonContainer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Scaricare un'icona chat
- Scaricare un'icona dal repository GitHub.
- Salvare l'icona in UILibraryQuickStart/app/src/main/res/drawable.
- Aprire l'icona e passare
android:fillColor
a@color/white
.
Inizializzare il composito
Per inizializzare la chiamata composita, passare a MainActivity
e aggiornare le impostazioni di connessione:
- Sostituire
TEAM_MEETING_LINK
con il collegamento alla riunione di Teams. - Sostituire
ACS_ENDPOINT
con l'endpoint della risorsa Servizi di comunicazione di Azure. - Sostituire
DISPLAY_NAME
con il proprio nome. - Sostituire
USER_ID
con l'ID utente Servizi di comunicazione di Azure. - Sostituire
USER_ACCESS_TOKEN
con il token.
Ottenere un thread di chat riunione di Teams per un utente Servizi di comunicazione di Azure
È possibile recuperare i dettagli delle riunioni di Teams usando le API Graph, come descritto nella documentazione di Graph. L'SDK per chiamate Servizi di comunicazione di Azure accetta un collegamento completo alla riunione di Teams o un ID riunione. Vengono restituiti come parte della onlineMeeting
risorsa, accessibile nella proprietà joinWebUrl .
Con le API Graph è anche possibile ottenere il threadID
valore. La risposta ha un chatInfo
oggetto che contiene il threadID
valore .
package com.example.uilibraryquickstart
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.ViewGroup
import android.widget.Button
import androidx.constraintlayout.widget.ConstraintLayout
import com.azure.android.communication.common.CommunicationTokenCredential
import com.azure.android.communication.common.CommunicationTokenRefreshOptions
import com.azure.android.communication.common.CommunicationUserIdentifier
import com.azure.android.communication.ui.calling.CallComposite
import com.azure.android.communication.ui.calling.CallCompositeBuilder
import com.azure.android.communication.ui.calling.models.CallCompositeCallScreenHeaderViewData
import com.azure.android.communication.ui.calling.models.CallCompositeCallScreenOptions
import com.azure.android.communication.ui.calling.models.CallCompositeCallStateCode
import com.azure.android.communication.ui.calling.models.CallCompositeCustomButtonViewData
import com.azure.android.communication.ui.calling.models.CallCompositeLocalOptions
import com.azure.android.communication.ui.calling.models.CallCompositeMultitaskingOptions
import com.azure.android.communication.ui.calling.models.CallCompositeTeamsMeetingLinkLocator
import com.azure.android.communication.ui.chat.ChatAdapter
import com.azure.android.communication.ui.chat.ChatAdapterBuilder
import com.azure.android.communication.ui.chat.presentation.ChatThreadView
import java.util.UUID
class MainActivity : AppCompatActivity() {
companion object {
private var callComposite: CallComposite? = null
private var chatAdapter: ChatAdapter? = null
}
private val displayName = "USER_NAME"
private val endpoint = "ACS_ENDPOINT"
private val teamsMeetingLink = "TEAM_MEETING_LINK"
private val threadId = "CHAT_THREAD_ID"
private val communicationUserId = "USER_ID"
private val userToken = "USER_ACCESS_TOKEN"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.startCallButton).setOnClickListener {
startCallComposite()
}
}
private fun startCallComposite() {
val communicationTokenRefreshOptions = CommunicationTokenRefreshOptions({ userToken }, true)
val communicationTokenCredential = CommunicationTokenCredential(communicationTokenRefreshOptions)
val locator = CallCompositeTeamsMeetingLinkLocator(teamsMeetingLink)
val localOptions = CallCompositeLocalOptions()
.setCallScreenOptions(
CallCompositeCallScreenOptions().setHeaderViewData(
CallCompositeCallScreenHeaderViewData().setCustomButtons(
listOf(
CallCompositeCustomButtonViewData(
UUID.randomUUID().toString(),
R.drawable.ic_fluent_chat_24_regular,
"Open Chat",
) {
callComposite?.sendToBackground()
showChatUI()
}
)
)
))
val callComposite = CallCompositeBuilder()
.applicationContext(this.applicationContext)
.credential(communicationTokenCredential)
.displayName(displayName)
.multitasking(CallCompositeMultitaskingOptions(true, true))
.build()
callComposite.addOnCallStateChangedEventHandler { callState ->
// When a user is admitted to the Teams meeting, the call state becomes connected.
// Only users admitted to the meeting can connect to the meeting's chat thread.
if (callState.code == CallCompositeCallStateCode.CONNECTED) {
connectChat()
}
}
callComposite.launch(this, locator, localOptions)
MainActivity.callComposite = callComposite
}
private fun connectChat() {
if (chatAdapter != null)
return
val communicationTokenRefreshOptions =
CommunicationTokenRefreshOptions( { userToken }, true)
val communicationTokenCredential =
CommunicationTokenCredential(communicationTokenRefreshOptions)
val chatAdapter = ChatAdapterBuilder()
.endpoint(endpoint)
.credential(communicationTokenCredential)
.identity(CommunicationUserIdentifier(communicationUserId))
.displayName(displayName)
.threadId(threadId)
.build()
chatAdapter.connect(applicationContext)
MainActivity.chatAdapter = chatAdapter
}
private fun showChatUI() {
chatAdapter?.let {
// Create Chat Composite View
val chatView = ChatThreadView(this, chatAdapter)
val chatContainer = findViewById<ConstraintLayout>(R.id.chatContainer)
chatContainer.removeAllViews()
chatContainer.addView(
chatView,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
}
}
}
Prerequisiti
- Un account Azure e una sottoscrizione di Azure attiva. Creare un account gratuitamente.
- Un Mac che esegue Xcode 13 o versione successiva e un certificato per sviluppatore valido installato nel keychain. È necessario che sia installato anche CocoaPods per recuperare le dipendenze.
- Una risorsa distribuita di Servizi di comunicazione di Azure.
- Token di accesso di Servizi di comunicazione di Azure.
Ottenere un esempio completo
È possibile ottenere un progetto di esempio completo da GitHub.
Configurare il progetto
Completare le sezioni seguenti per configurare il progetto della guida introduttiva.
Creare un nuovo progetto Xcode
In Xcode, creare un nuovo progetto:
Nel menu File selezionare Nuovo>Progetto.
In Scegli un modello per il nuovo progetto, selezionare la piattaforma iOS e scegliere il modello applicazione App. La guida introduttiva utilizza gli storyboard UIKit. La guida introduttiva non crea test, quindi è possibile deselezionare la casella di controllo Includi test.
In Scegli le opzioni per il nuovo progetto, immettere UILibraryQuickStart per il nome del prodotto. Per l'interfaccia, selezionare Storyboard.
Installare il pacchetto e le relative dipendenze
(Facoltativo) Per MacBook con M1, installare e abilitare Rosetta in Xcode.
Nella directory radice del progetto, eseguire
pod init
per creare un Podfile. Se si verifica un errore, aggiornare CocoaPods alla versione corrente.Aggiungere il codice seguente al Podfile. Sostituire
UILibraryQuickStart
con il nome del progetto.platform :ios, '15.0' target 'UILibraryQuickStart' do use_frameworks! pod 'AzureCommunicationUICalling', '1.12.0-beta.1' pod 'AzureCommunicationUIChat', '1.0.0-beta.4' end
Eseguire
pod install --repo-update
.In Xcode aprire il file generated.xcworkspace .
Richiedere l'accesso all'hardware del dispositivo
Per accedere all'hardware del dispositivo, incluso il microfono e la webcam, aggiornare l'elenco delle proprietà delle informazioni dell'app. Impostare il valore associato su una stringa inclusa nella finestra di dialogo usata dal sistema per richiedere l'accesso dall'utente.
Fare clic con il pulsante destro del mouse sulla voce
Info.plist
dell'albero del progetto e scegliere Open As>Source Code (Apri come > Codice sorgente). Aggiungere le righe seguenti alla sezione di primo livello<dict>
e quindi salvare il file.<key>NSCameraUsageDescription</key> <string></string> <key>NSMicrophoneUsageDescription</key> <string></string>
Ecco un esempio del codice sorgente
Info.plist
in un file Xcode:Per verificare che le richieste di autorizzazione del dispositivo vengano aggiunte correttamente, selezionare Apri come>Elenco di proprietà. Verificare che l'elenco delle proprietà delle informazioni sia simile all'esempio seguente:
Disattivare Bitcode
Nel progetto Xcode, in Impostazioni di compilazione, impostare l'opzione Abilita Bitcode su No. Per individuare l'impostazione, modificare il filtro da Base a Tutto o usare la barra di ricerca.
Scaricare un'icona chat
- Scaricare un'icona dal repository GitHub.
- Aprire il file scaricato e passare
fill
afill="#FFFFFF"
. - In Xcode passare ad Asset. Creare un nuovo set di immagini e denominarlo ic_fluent_chat_24_regular. Selezionare il file scaricato come icona universale.
Inizializzare il composito
Per inizializzare il composito, passare a ViewController
e aggiornare le impostazioni di connessione:
- Sostituire
TEAM_MEETING_LINK
con il collegamento alla riunione di Teams. - Sostituire
ACS_ENDPOINT
con l'endpoint della risorsa Servizi di comunicazione di Azure. - Sostituire
DISPLAY_NAME
con il proprio nome. - Sostituire
USER_ID
con l'ID utente Servizi di comunicazione di Azure. - Sostituire
USER_ACCESS_TOKEN
con il token.
Ottenere un thread di chat riunione di Teams per un utente Servizi di comunicazione di Azure
È possibile recuperare i dettagli delle riunioni di Teams usando le API Graph, come descritto nella documentazione di Graph. L'SDK per chiamate Servizi di comunicazione di Azure accetta un collegamento completo alla riunione di Teams o un ID riunione. Vengono restituiti come parte della onlineMeeting
risorsa, accessibile nella proprietà joinWebUrl .
Con le API Graph è anche possibile ottenere il threadID
valore. La risposta ha un chatInfo
oggetto che contiene il threadID
valore .
import UIKit
import AzureCommunicationCalling
import AzureCommunicationUICalling
import AzureCommunicationUIChat
class ViewController: UIViewController {
private let displayName = "USER_NAME"
private let endpoint = "ACS_ENDPOINT"
private let teamsMeetingLink = "TEAM_MEETING_LINK"
private let chatThreadId = "CHAT_THREAD_ID"
private let communicationUserId = "USER_ID"
private let userToken = "USER_ACCESS_TOKEN"
private var callComposite: CallComposite?
private var chatAdapter: ChatAdapter?
private var chatCompositeViewController: ChatCompositeViewController?
private var startCallButton: UIButton?
private var chatContainerView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
initControlBar()
}
@objc private func startCallComposite() {
let callCompositeOptions = CallCompositeOptions(
enableMultitasking: true,
enableSystemPictureInPictureWhenMultitasking: true,
displayName: displayName)
let communicationTokenCredential = try! CommunicationTokenCredential(token: userToken)
let callComposite = self.callComposite ?? CallComposite(credential: communicationTokenCredential, withOptions: callCompositeOptions)
self.callComposite = callComposite
callComposite.events.onCallStateChanged = { [weak self] callState in
if callState.requestString == CallState.connected.requestString {
self?.connectChat()
}
}
let chatCustomButton = CustomButtonViewData(
id: UUID().uuidString,
image: UIImage(named: "ic_fluent_chat_24_regular")!,
title: "Chat") { [weak self] _ in
self?.callComposite?.isHidden = true
self?.showChat()
}
let callScreenHeaderViewData = CallScreenHeaderViewData(customButtons: [chatCustomButton])
let localOptions = LocalOptions(callScreenOptions: CallScreenOptions(headerViewData: callScreenHeaderViewData))
callComposite.launch(locator: .teamsMeeting(teamsLink: teamsMeetingLink), localOptions: localOptions)
}
@objc private func connectChat() {
let communicationIdentifier = CommunicationUserIdentifier(communicationUserId)
guard let communicationTokenCredential = try? CommunicationTokenCredential(
token: userToken) else {
return
}
self.chatAdapter = ChatAdapter(
endpoint: endpoint,
identifier: communicationIdentifier,
credential: communicationTokenCredential,
threadId: chatThreadId,
displayName: displayName)
Task { @MainActor in
guard let chatAdapter = self.chatAdapter else {
return
}
try await chatAdapter.connect()
}
}
@objc private func showChat() {
guard let chatAdapter = self.chatAdapter,
let chatContainerView = self.chatContainerView,
self.chatCompositeViewController == nil else {
return
}
let chatCompositeViewController = ChatCompositeViewController(with: chatAdapter)
self.addChild(chatCompositeViewController)
chatContainerView.addSubview(chatCompositeViewController.view)
chatCompositeViewController.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
chatCompositeViewController.view.topAnchor.constraint(equalTo: chatContainerView.topAnchor),
chatCompositeViewController.view.bottomAnchor.constraint(equalTo: chatContainerView.bottomAnchor),
chatCompositeViewController.view.leadingAnchor.constraint(equalTo: chatContainerView.leadingAnchor),
chatCompositeViewController.view.trailingAnchor.constraint(equalTo: chatContainerView.trailingAnchor)
])
chatCompositeViewController.didMove(toParent: self)
self.chatCompositeViewController = chatCompositeViewController
}
private func initControlBar() {
let startCallButton = UIButton()
self.startCallButton = startCallButton
startCallButton.layer.cornerRadius = 10
startCallButton.contentEdgeInsets = UIEdgeInsets(top: 6, left: 16, bottom: 6, right: 16)
startCallButton.backgroundColor = .systemBlue
startCallButton.setTitle("Call", for: .normal)
startCallButton.addTarget(self, action: #selector(startCallComposite), for: .touchUpInside)
startCallButton.translatesAutoresizingMaskIntoConstraints = false
let margin: CGFloat = 32.0
let buttonsContainerView = UIView()
buttonsContainerView.backgroundColor = .clear
let buttonsStackView = UIStackView(arrangedSubviews: [startCallButton])
buttonsStackView.axis = .horizontal
buttonsStackView.alignment = .center
buttonsStackView.distribution = .equalSpacing
buttonsStackView.spacing = 10
buttonsStackView.translatesAutoresizingMaskIntoConstraints = false
buttonsStackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
buttonsContainerView.addSubview(buttonsStackView)
buttonsContainerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
buttonsStackView.topAnchor.constraint(equalTo: buttonsContainerView.topAnchor, constant: 8),
buttonsStackView.bottomAnchor.constraint(equalTo: buttonsContainerView.bottomAnchor, constant: -8),
buttonsStackView.leadingAnchor.constraint(equalTo: buttonsContainerView.leadingAnchor, constant: 16),
])
let chatContainerView = UIView()
self.chatContainerView = chatContainerView
let verticalStackView = UIStackView(arrangedSubviews: [
buttonsContainerView,
chatContainerView
])
verticalStackView.axis = .vertical
verticalStackView.alignment = .fill
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(verticalStackView)
let margins = view.safeAreaLayoutGuide
let constraints = [
verticalStackView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
verticalStackView.trailingAnchor.constraint(equalTo: margins.trailingAnchor),
verticalStackView.topAnchor.constraint(equalTo: margins.topAnchor, constant: margin),
verticalStackView.bottomAnchor.constraint(equalTo: margins.bottomAnchor, constant: -margin)
]
NSLayoutConstraint.activate(constraints)
}
}
Eseguire il codice
Eseguire il codice per compilare ed eseguire l'app nel dispositivo.
Altre funzionalità
L'elenco dei casi d'uso contiene informazioni dettagliate su altre funzionalità.
Aggiungere notifiche all'app per dispositivi mobili
Servizi di comunicazione di Azure si integra con Griglia di eventi di Azure e Hub di notifica di Azure, in modo da poter aggiungere notifiche push alle app in Azure. È possibile usare le notifiche push per inviare informazioni dall'applicazione ai dispositivi mobili degli utenti. Una notifica push può mostrare una finestra di dialogo, riprodurre un suono o visualizzare un'interfaccia utente di chiamata in ingresso.