Quickstart: Add joining a Teams meeting to your app

Important

This feature of Azure Communication Services is currently in public preview. Preview APIs and SDKs are provided without a service-level agreement, and are not recommended for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see Supplemental Terms of Use for Microsoft Azure Previews.

Get started with Azure Communication Services by using the Communication Services Teams Embed SDK to add teams meetings to your app.

In this quickstart, you'll learn how to join a Microsoft Teams meeting using the Azure Communication Services Teams Embed library for Android.

Sample Code

You can download the sample app from GitHub.

Prerequisites

Setting up

Create an Android app with an empty activity

From Android Studio, select Start a new Android Studio project.

Screenshot showing the 'Start a new Android Studio Project' button selected in Android Studio.

Select "Empty Activity" project template under "Phone and Tablet".

Screenshot showing the 'Empty Activity' option selected in the Project Template Screen.

Name the project TeamsEmbedAndroidGettingStarted, set language to java and select Minimum SDK of "API 21: Android 5.0 (Lollipop)" or greater.

Screenshot showing the 'Empty Activity' option selected in the Project Template Screen 2.

Install the Azure package

In your app level (app folder) build.gradle, add the following lines to the dependencies and android sections

android {
    ...
    packagingOptions {
        pickFirst  'META-INF/*'
    }
}
dependencies {
    ...
    implementation 'com.azure.android:azure-communication-common:1.0.0'
    ...
}

Update the values on the build.gradle file

 buildTypes {
        release {
        ...
            minifyEnabled true
            shrinkResources true
        ...
    }
}

Install the Teams Embed package

Download the MicrosoftTeamsSDK package.

Then unzip the MicrosoftTeamsSDK folder into your projects app folder. Ex. TeamsEmbedAndroidGettingStarted/app/MicrosoftTeamsSDK.

Add Teams Embed package to your build.gradle

In your app-level build.gradle, add the following line at the end of the file:

apply from: 'MicrosoftTeamsSDK/MicrosoftTeamsSDK.gradle'

Sync project with gradle files.

Create application class

Create new Java class file named TeamsEmbedAndroidGettingStarted. This class will be the application class, which must extend TeamsSDKApplication. Android Documentation

Screenshot showing where to create application class in Android Studio

package com.microsoft.teamsembedandroidgettingstarted;

import com.microsoft.teamssdk.app.TeamsSDKApplication;

public class TeamsEmbedAndroidGettingStarted extends TeamsSDKApplication {
}

Update themes

Set the style name to AppTheme in both your theme.xml and theme.xml (night) files.

Screenshot showing the theme.xml files in Android Studio

<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

Add permissions to application manifest

Add the RECORD_AUDIO permission to your Application Manifest (app/src/main/AndroidManifest.xml):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yourcompany.TeamsEmbedAndroidGettingStarted">
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <application

Add app name and theme to application manifest

Add `xmlns:tools="http://schemas.android.com/tools" to the manifest.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.yourcompany.TeamsEmbedAndroidGettingStarted">

Add .TeamsEmbedAndroidGettingStarted to android:name, android:name to tools:replace, and change the android:theme to @style/AppTheme

<application
    android:name=".TeamsEmbedAndroidGettingStarted"
    tools:replace="android:name"
    android:theme="@style/AppTheme"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true">

Set up the layout for the app

Create a button with an ID of join_meeting. Navigate to the layout file (app/src/main/res/layout/activity_main.xml) and replace the content of file with the following code:

<?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">

    <Button
        android:id="@+id/join_meeting"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Join Meeting"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Create the main activity scaffolding and bindings

With the layout created, the basic scaffolding of the activity along with required bindings can be added. The activity will handle requesting runtime permissions, creating the meeting client, and joining a meeting when the button is pressed. Each will be covered in its own section.

The onCreate method will be overridden to add the bindings for the Join Meeting button. This will occur only once when the activity is created. For more information on onCreate, see the guide Understand the Activity Lifecycle.

Navigate to MainActivity.java and replace the content with the following code:

package com.yourcompany.teamsembedandroidgettingstarted;

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.Toast;

import com.azure.android.communication.common.CommunicationTokenCredential;
import com.azure.android.communication.common.CommunicationTokenRefreshOptions;
import com.azure.android.communication.ui.meetings.MeetingUIClientJoinOptions;
import com.azure.android.communication.ui.meetings.MeetingUIClient;
import com.azure.android.communication.ui.meetings.MeetingUIClientTeamsMeetingLinkLocator;

import java.util.ArrayList;
import java.util.concurrent.Callable;

public class MainActivity extends AppCompatActivity {

    private final String displayName = "John Smith";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Button joinMeetingButton = findViewById(R.id.join_meeting);
        joinMeetingButton.setOnClickListener(l -> joinMeeting());
    }
    
    private void joinMeeting() {
    // See section on joining a meeting
    }

    private MeetingUIClient createMeetingUIClient() {
        // See section on creating meeting ui client
    }

    private void getAllPermissions() {
        // See section on getting permissions
    }
}

Request permissions at runtime

For Android 6.0 and higher (API level 23) and targetSdkVersion 23 or higher, permissions are granted at runtime instead of when the app is installed. To request permissions, getAllPermissions can be implemented to call ActivityCompat.checkSelfPermission and ActivityCompat.requestPermissions for each required permission.

/**
 * Request each required permission if the app doesn't already have it.
 */
private void getAllPermissions() {
    String[] requiredPermissions = new String[]{Manifest.permission.RECORD_AUDIO};
    ArrayList<String> permissionsToAskFor = new ArrayList<>();
    for (String permission : requiredPermissions) {
        if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
            permissionsToAskFor.add(permission);
        }
    }
    if (!permissionsToAskFor.isEmpty()) {
        ActivityCompat.requestPermissions(this, permissionsToAskFor.toArray(new String[0]), 202);
    }
}

Note

When designing your app, consider when these permissions should be requested. Permissions should be requested as they are needed, not ahead of time. For more information, see the Android Permissions Guide.

Object model

The following classes and interfaces handle some of the major features of the Azure Communication Services Teams Embed library:

Name Description
MeetingUIClient The MeetingUIClient is the main entry point to the Teams Embed library.
MeetingUIClientJoinOptions MeetingUIClientJoinOptions are used for configurable options such as display name.
MeetingUIClientTeamsMeetingLinkLocator MeetingUIClientTeamsMeetingLinkLocator is used to set the meeting URL for joining a meeting.
MeetingUIClientGroupCallLocator MeetingUIClientGroupCallLocator is used for setting the group ID to join.
MeetingUIClientIconType MeetingUIClientIconType is used to specify which icons could be replaced with app-specific icon.
MeetingUIClientCall MeetingUIClientCall describes the call and provides APIs to control it.
MeetingUIClientCallState The MeetingUIClientCallState is used to for reporting call state changes. The options are as follows: CONNECTING, WAITING_IN_LOBBY, CONNECTED, and ENDED.
MeetingUIClientAudioRoute MeetingUIClientAudioRoute is used for local audio routes like Earpiece or SpeakerOn.
MeetingUIClientLayoutMode MeetingUIClientLayoutMode is used for allowing to select different in call UI modes.
MeetingUIClientAvatarSize MeetingUIClientAvatarSize is an enum to denote different avatar sizes that can be requested by MeetingUIClientCallIdentityProvider
MeetingUIClientCallEventListener The MeetingUIClientCallEventListener is used to receive events, such as changes in call state.
MeetingUIClientCallIdentityProvider The MeetingUIClientCallIdentityProvider is used to map user details to the users in a meeting.
MeetingUIClientCallUserEventListener The MeetingUIClientCallUserEventListener provides information about user actions in the UI.

Create a MeetingClient from the user access token

An authenticated meeting client can be instantiated with a user access token. This token is generated by a service with authentication specific to the application. To learn more about user access tokens, check the User Access Tokens guide. For the quickstart, replace <USER_ACCESS_TOKEN> with a user access token generated for your Azure Communication Service resource.

private MeetingUIClient createMeetingUIClient() { 
    try {
        CommunicationTokenRefreshOptions refreshOptions = new CommunicationTokenRefreshOptions(tokenRefresher, true, "<USER_ACCESS_TOKEN>");
        CommunicationTokenCredential credential = new CommunicationTokenCredential(refreshOptions);
        return new MeetingUIClient(credential);
    } catch (Exception ex) {
        Toast.makeText(getApplicationContext(), "Failed to create meeting ui client: " + ex.getMessage(), Toast.LENGTH_SHORT).show();
    }
}

Setup Token refreshing

Create a Callable tokenRefresher method. Then create a fetchToken method to get the user token. You can find instructions on how to do so here

Callable<String> tokenRefresher = () -> {
    return fetchToken();
};

public String fetchToken() {
    // Get token
    return USER_ACCESS_TOKEN;
}

Start a meeting using the meeting client

The joinMeeting method is set as the action that will be performed when the Join Meeting button is tapped. Joining a meeting can be done via the MeetingUIClient, and just requires a MeetingUIClientTeamsMeetingLinkLocator and the MeetingUIClientJoinOptions. Note to replace <MEETING_URL> with a Microsoft Teams meeting link.

/**
 * Join a meeting with a meetingURL.
 */
private void joinMeeting() {
    getAllPermissions();
    MeetingUIClient meetingUIClient = createMeetingUIClient();
    
    MeetingUIClientTeamsMeetingLinkLocator meetingUIClientTeamsMeetingLinkLocator = new MeetingUIClientTeamsMeetingLinkLocator(<MEETING_URL>);
    
    MeetingUIClientJoinOptions meetingJoinOptions = new MeetingUIClientJoinOptions(displayName, false);
    
    try {
        meetingUIClient.join(meetingUIClientTeamsMeetingLinkLocator, meetingJoinOptions);
    } catch (Exception ex) {
        Toast.makeText(getApplicationContext(), "Failed to join meeting: " + ex.getMessage(), Toast.LENGTH_SHORT).show();
    }
}

A Microsoft Teams meeting link can be retrieved using Graph APIs. The steps to retrieve a meeting link are detailed in Graph documentation. The Communication Services Calling SDK accepts a full Teams meeting link. This link is returned as part of the onlineMeeting resource, accessible under the joinWebUrl property You can also get the required meeting information from the Join Meeting URL in the Teams meeting invite itself.

Launch the app and join a meeting

The app can now be launched using the "Run App" button on the toolbar (Shift+F10).

Screenshot showing the completed application.

Screenshot showing the completed application after meeting has been joined.

Add localization support based on your app

The Microsoft Teams SDK supports over 100 strings in over 50 languages. By default only English is enabled. The rest of them can be enabled in the gradle file.

Add localizations to the SDK based on what your app supports

  1. Determine the list of languages your app supports
  2. Open MicrosoftTeamsSDK.gradle file
  3. In the defaultConfig block, the resConfigs property is set to "en" by default. Add the languages that your app needs. Reference: Android Documentation

In this quickstart, you'll learn how to join a Microsoft Teams meeting using the Azure Communication Services Teams Embed library for iOS.

Sample Code

You can download the sample app from GitHub.

Prerequisites

To complete this quickstart, you’ll need the following prerequisites:

Setting up

Creating the Xcode project

In Xcode, create a new iOS project and select the App template. We will be using UIKit storyboards. You're not going to create tests during this quickstart. Feel free to uncheck Include Tests.

Screenshot showing the New Project template selection within Xcode.

Name the project TeamsEmbedGettingStarted.

Screenshot showing the New Project details within Xcode.

Install the package and dependencies with CocoaPods

  1. Create a Podfile for your application:
platform :ios, '12.0'
use_frameworks!

target 'TeamsEmbedGettingStarted' do
    pod 'AzureCommunicationCommon', '1.0.0'
end

azure_libs = [
'AzureCommunicationCommon',
'AzureCore']

post_install do |installer|
    installer.pods_project.targets.each do |target|
    if azure_libs.include?(target.name)
        puts "Adding BUILD_LIBRARY_FOR_DISTRIBUTION to #{target.name}"
        target.build_configurations.each do |config|
        xcconfig_path = config.base_configuration_reference.real_path
        File.open(xcconfig_path, "a") {|file| file.puts "BUILD_LIBRARY_FOR_DISTRIBUTION = YES"}
        end
    end
    end
end
  1. Run pod install.
  2. Open the generated .xcworkspace with Xcode.

Request access to the microphone, camera, etc.

To access the device's hardware, update your app's Information Property List. Set the associated value to a string that will be included in the dialog the system uses to request access from the user.

Right-click the Info.plist entry of the project tree and select Open As > Source Code. Add the following lines the top level <dict> section, and then save the file.

<key>NSCameraUsageDescription</key>
<string></string>
<key>NSContactsUsageDescription</key>
<string></string>
<key>NSMicrophoneUsageDescription</key>
<string></string>
<key>NSPhotoLibraryUsageDescription</key>
<string></string>

Add the Teams Embed framework

  1. Download the Teams Embed iOS SDK Bundle and uncompress it.
  2. Create a Frameworks folder in the project root. Ex. \TeamsEmbedGettingStarted\Frameworks\
  3. Copy the downloaded AzureMeetingUIClient.xcframework and TeamsAppSDK.xcframework and other frameworks provided in the release bundle to the folder mentioned above.
  4. Add the frameworks to the project target under the general tab. Use the Add Other -> Add Files... to navigate to the framework files and add them.
  5. Select Embed & Sign for all of the added frameworks.

Screenshot showing the added frameworks in Xcode.

  1. If it isn't already, add $(PROJECT_DIR)/Frameworks to Framework Search Paths under the project target build settings tab. To find the setting, change the filter from basic to all. You can also use the search bar on the right.

Screenshot showing the framework search path in Xcode.

Turn off Bitcode

Set Enable Bitcode option to No in the project Build Settings. To find the setting, you have to change the filter from Basic to All, you can also use the search bar on the right.

Screenshot showing the BitCode option in Xcode.

Turn on Voice over IP background mode.

Select your app target and click the Capabilities tab.

Turn on Background Modes by clicking the + Capabilities at the top and select Background Modes.

Select checkbox for Voice over IP.

Screenshot showing enabled VOIP in Xcode.

Add a window reference to AppDelegate

Open your project's AppDelegate.swift file and add a reference for 'window'.

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

Add a button to the ViewController

Create a button in the viewDidLoad callback in ViewController.swift.

override func viewDidLoad() {
    super.viewDidLoad()
    
    let button = UIButton(frame: CGRect(x: 100, y: 100, width: 200, height: 50))
    button.backgroundColor = .black
    button.setTitle("Join Meeting", for: .normal)
    button.addTarget(self, action: #selector(joinMeetingTapped), for: .touchUpInside)
    
    button.translatesAutoresizingMaskIntoConstraints = false
    self.view.addSubview(button)
    button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}

Create an outlet for the button in ViewController.swift.

@IBAction func joinMeetingTapped(_ sender: UIButton) {

}

Set up the app framework

Open your project's ViewController.swift file and add an import declaration to the top of the file to import the AzureCommunicationCommon library and the MeetingUIClient.

import UIKit
import AzureCommunicationCommon
import AzureMeetingUIClient

Replace the implementation of the ViewController class with a simple button to allow the user to join a meeting. We will attach business logic to the button in this quickstart.

class ViewController: UIViewController {

    private var meetingUIClient: MeetingUIClient?
    private var meetingUIClientCall: MeetingUIClientCall?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Initialize meetingClient
    }

    @IBAction func joinMeetingTapped(_ sender: UIButton) {
        joinMeeting()
    }
    
    private func joinMeeting() {
        // Add join meeting logic
    }
}

Object model

The following classes and interfaces handle some of the major features of the Azure Communication Services Teams Embed library:

Name Description
MeetingUIClient The MeetingUIClient is the main entry point to the Teams Embed library.
MeetingUIClientMeetingJoinOptions MeetingUIClientMeetingJoinOptions are used for configurable options such as display name.
MeetingUIClientGroupCallJoinOptions MeetingUIClientGroupCallJoinOptions are used for configurable options such as display name.
MeetingUIClientTeamsMeetingLinkLocator MeetingUIClientTeamsMeetingLinkLocator is used to set the meeting URL for joining a meeting.
MeetingUIClientGroupCallLocator MeetingUIClientGroupCallLocator is used for setting the group ID to join.
MeetingUIClientIconType MeetingUIClientIconType is used to specify which icons could be replaced with app-specific icon.
MeetingUIClientCall MeetingUIClientCall describes the call and provides APIs to control it.
MeetingUIClientCallState The MeetingUIClientCallState is used to for reporting call state changes. The options are as follows: connecting, waitingInLobby, connected, and ended.
MeetingUIClientAudioRoute MeetingUIClientAudioRoute is used for local audio routes like Earpiece or SpeakerOn.
MeetingUIClientLayoutMode MeetingUIClientLayoutMode is used for allowing to select different in call UI modes.
MeetingUIClientAvatarSize MeetingUIClientAvatarSize is an enum to denote different avatar sizes that can be requested by MeetingUIClientCallIdentityProvider.
MeetingUIClientCallDelegate The MeetingUIClientDelegate is used to receive events, such as changes in call state.
MeetingUIClientCallIdentityProviderDelegate The MeetingUIClientIdentityProviderDelegate is used to map user details to the users in a meeting.
MeetingUIClientCallUserEventDelegate The MeetingUIClientUserEventDelegate provides information about user actions in the UI.
MeetingUIClientCallRosterDelegate The MeetingUIClientCallRosterDelegate provides information about call roster.

Create and Authenticate the client

Initialize a MeetingUIClient instance with a User Access Token, which will enable us to join meetings. Add the following code to the viewDidLoad callback in ViewController.swift:

do {
    let communicationTokenRefreshOptions = CommunicationTokenRefreshOptions(initialToken: "<USER_ACCESS_TOKEN>", refreshProactively: true, tokenRefresher: fetchTokenAsync(completionHandler:))
	let credential = try CommunicationTokenCredential(withOptions: communicationTokenRefreshOptions)
    meetingUIClient = MeetingUIClient(with: credential)
}
catch {
    print("Failed to create communication token credential")
}

Replace <USER_ACCESS_TOKEN> with a valid user access token for your resource. Refer to the user access token documentation if you don't already have a token available.

Setup Token refreshing

Create a fetchTokenAsync method. Then add your fetchToken logic to get the user token.

private func fetchTokenAsync(completionHandler: @escaping TokenRefreshHandler) {
    func getTokenFromServer(completionHandler: @escaping (String) -> Void) {
        completionHandler("<USER_ACCESS_TOKEN>")
    }
    getTokenFromServer { newToken in
        completionHandler(newToken, nil)
    }
}

Replace <USER_ACCESS_TOKEN> with a valid user access token for your resource.

Join a meeting

The join method is set as the action that will be performed when the Join Meeting button is tapped. Update the implementation to join a meeting with the MeetingUIClient:

private func joinMeeting() {
    let meetingJoinOptions = MeetingUIClientMeetingJoinOptions(displayName: "John Smith", enablePhotoSharing: false, enableNamePlateOptionsClickDelegate: false, enableCallStagingScreen: false, enableCallRosterDelegate: false)
    let meetingLocator = MeetingUIClientTeamsMeetingLinkLocator(meetingLink: "<MEETING_URL>")
    meetingUIClient?.join(meetingLocator: meetingLocator, joinCallOptions: meetingJoinOptions, completionHandler: { (meetingUIClientCall: MeetingUIClientCall?, error: Error?) in
        if (error != nil) {
            print("Join meeting failed: \(error!)")
        }
        else {
            if (meetingUIClientCall != nil) {
                self.meetingUIClientCall? = meetingUIClientCall
            }
        }
    })
}

Note to replace <MEETING URL> with a Microsoft Teams meeting link.

The completion handler will return error in case the operation fails or it will return MeetingUIClientCall if it succeeded. Use the MeetingUIClientCall to control the call.

A Microsoft Teams meeting link can be retrieved using Graph APIs. This process is detailed in Graph documentation. The Communication Services Calling SDK accepts a full Microsoft Teams meeting link. This link is returned as part of the onlineMeeting resource, accessible under the joinWebUrl property You can also get the required meeting information from the Join Meeting URL in the Teams meeting invite itself.

Run the code

You can build and run your app on iOS simulator by selecting Product > Run or by using the (⌘-R) keyboard shortcut.

Final look and feel of the quick start app

Final look and feel once the meeting has been joined

Note

The first time you join a meeting, the system will prompt you for access to the microphone. In a production application, you should use the AVAudioSession API to check the permission status and gracefully update your application's behavior when permission is not granted.

Add localization support based on your app

The Microsoft Teams SDK supports over 100 strings and resources. The framework bundle contains Base and English languages. The rest of them are included in the Localizations.zip file included with the package.

Add localizations to the SDK based on what your app supports

  1. Determine what kind of localizations your application supports from the app Xcode Project > Info > Localizations list
  2. Unzip the Localizations.zip included with the package
  3. Copy the localization folders from the unzipped folder based on what your app supports to the root of the TeamsAppSDK.framework

Clean up resources

If you want to clean up and remove a Communication Services resource, you can delete the resource or resource group. Deleting the resource group also deletes any other resources associated with it. Learn more about cleaning up resources.