Create a Basic Audio Conference Application for Android
Prerequisites
Make sure that you have:
- A Dolby.io account
- A working webcam and a microphone
- Android Studio
For reference, see the GitHub sample repository.
Procedure
Create your project
Open Android Studio and create a new Java
project. Select the “Empty activity” template. In this example, only default options are included.
1. Add the SDK dependency.
Include the Dolby.io Communications SDK for Android as a dependency in the app/build.gradle
file, in the dependencies
section:
implementation "io.dolby:sdk:3.10.1"
2. Use viewBinding
to facilitate the integration of the SDK and manage injection, in the app/build.gradle
file, in the android
section:
buildFeatures {
viewBinding = true
}
3. Make sure to not use Android SDK version older than 21. The SDK is only compatible with android 21+
. This change is for the android
section in the file app/build.gradle
.
compileSdkVersion 33
buildToolsVersion "31.0.0"
defaultConfig {
minSdkVersion 21
targetSdkVersion 31
}
4. Make sure that a compilation toolchain uses the Java 11. This change is for the android
section in the file app/build.gradle
.
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
5. Android Studio displays a banner above the text editor that informs that the Gradle files have changed. Click the “Sync Now” link.
6. To update the layout, edit the activity_main.xml
file from the app/src/main/res/layout/
folder. In Android Studio, this file is found in app/res/layout
. Modify its content as in the following example:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Step 1. Put the layout changes for the open/close session step here -->
<!-- Step 2. Put the layout changes for the join conference step here -->
<!-- Step 3. Put the layout changes for the video step here -->
<!-- Step 4. Put the layout changes for the view participants step here -->
<!-- Step 5. Put the layout changes for the screen sharing step here -->
<!-- Step 6. Put the layout changes for the recording step here -->
</LinearLayout>
</ScrollView>
7. Edit the MainActivity.java
file and add the following imports:
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import com.example.myapplication.databinding.ActivityMainBinding;
import com.voxeet.VoxeetSDK;
import com.voxeet.android.media.MediaStream;
import com.voxeet.android.media.stream.MediaStreamType;
import com.voxeet.promise.solve.ErrorPromise;
import com.voxeet.promise.solve.ThenPromise;
import com.voxeet.sdk.events.promises.ServerErrorException;
import com.voxeet.sdk.events.v2.ParticipantAddedEvent;
import com.voxeet.sdk.events.v2.ParticipantUpdatedEvent;
import com.voxeet.sdk.events.v2.StreamAddedEvent;
import com.voxeet.sdk.events.v2.StreamRemovedEvent;
import com.voxeet.sdk.events.v2.StreamUpdatedEvent;
import com.voxeet.sdk.json.ParticipantInfo;
import com.voxeet.sdk.json.RecordingStatusUpdatedEvent;
import com.voxeet.sdk.json.internal.ParamsHolder;
import com.voxeet.sdk.models.Conference;
import com.voxeet.sdk.models.Participant;
import com.voxeet.sdk.services.builders.ConferenceCreateOptions;
import com.voxeet.sdk.services.builders.ConferenceJoinOptions;
import com.voxeet.sdk.services.conference.information.ConferenceInformation;
import com.voxeet.sdk.services.screenshare.RequestScreenSharePermissionEvent;
import com.voxeet.sdk.services.screenshare.ScreenCapturerService;
import com.voxeet.sdk.views.VideoView;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
8. To prepare events and logic integration, create a list of views responsible for keeping the views enabled in the following modes:
- when there are no connected sessions
- when a session is connected
- when a conference is connected
- when there are no connected conferences
- when your video is not started
- when your video is started
- when you are sharing your screen
- when you are not sharing your screen
Edit the MainActivity
class in MainActivity.java
and add the following values:
public class MainActivity extends AppCompatActivity {
protected List<View> views = new ArrayList<>();
protected List<View> buttonsNotLoggedIn = new ArrayList<>();
protected List<View> buttonsInConference = new ArrayList<>();
protected List<View> buttonsNotInConference = new ArrayList<>();
protected List<View> buttonsInOwnVideo = new ArrayList<>();
protected List<View> buttonsNotInOwnVideo = new ArrayList<>();
protected List<View> buttonsInOwnScreenShare = new ArrayList<>();
protected List<View> buttonsNotInOwnScreenShare = new ArrayList<>();
...
}
9. To simplify creating conferences, add the following code to the MainActivity
class. The onCreate
method created with the project is replaced.
- Overridden
onResume
method to update views. - A method for views updates and management.
- The default error management method.
- Two methods for managing contextual lists of views.
public class MainActivity extends AppCompatActivity {
...
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
// All the logic of the onCreate will be put after this comment
}
@Override
protected void onResume() {
super.onResume();
// Here will be put the permission check
// We update the various views to enable or disable the ones we want to
updateViews();
}
private void updateViews() {
// This method will be updated step by step
}
private ErrorPromise error() {
return error -> {
Toast.makeText(MainActivity.this, "ERROR...", Toast.LENGTH_SHORT).show();
error.printStackTrace();
updateViews();
};
}
private void setEnabled(List<View> views, boolean enabled) {
for (View view : views) view.setEnabled(enabled);
}
private MainActivity add(List<View> list, int id) {
list.add(findViewById(id));
return this;
}
}
Initialize the SDK with your Dolby.io credentials
Initialize the SDK using the secure authentication method that uses a token in the application. For more information, see the Initializing the SDK document. For the purpose of this example, we are using a client access token generated from the Dolby.io dashboard. It is recommended that you create a new Sample
application for this tutorial.
1. Add the following code to the onCreate
method from the MainActivity.java
file. Replace the TestClientAccessToken
string with the token generated from the Dolby.io dashboard.
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Generate a client access token from the Dolby.io dashboard and insert into accessToken variable
String accessToken = "<ClientAccessToken>";
VoxeetSDK.initialize(accessToken, (isExpired, tokenCallback) -> {
tokenCallback.ok(accessToken);
});
}
2. Add the following lines at the end of onResume
:
@Override
protected void onResume() {
super.onResume();
...
// Register the current activity in the SDK
VoxeetSDK.instance().register(this);
}
3. Unregister from the SDK when the MainActivity
is in the background by adding onPause
.
@Override
protected void onPause() {
// Register the current activity in the SDK
VoxeetSDK.instance().unregister(this);
super.onPause();
}
Open and close a session
To allow creating and joining conferences, log in with a user name. In this tutorial, random user names are assigned.
1. To modify the layout, edit the main_activity.xml
file, adding the following content for Step 1:
<LinearLayout ...>
<!-- Step 1. Put the layout changes for the open/close session step here -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="user session" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/user_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="log in" />
<Button
android:id="@+id/logout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="logout" />
</LinearLayout>
</LinearLayout>
<!-- Step 2. ... -->
</LinearLayout>
2. Modify the interface linking in the MainActivity
class in MainActivity.java
:
- New methods for
MainActivity
:
public void onLogin() {
}
public void onLogout() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Add onClickListeners for onLogin() and onLogout()
binding.login.setOnClickListener(_view -> onLogin());
binding.logout.setOnClickListener(_view -> onLogout());
- Add the following code to the
onCreate
method:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Adding the user_name, login and logout views related to the open/close and conference flow
add(views, R.id.login);
add(views, R.id.logout);
add(buttonsNotLoggedIn, R.id.login);
add(buttonsNotLoggedIn, R.id.user_name);
add(buttonsInConference, R.id.logout);
add(buttonsNotInConference, R.id.logout);
// Set a random user name
String[] avengersNames = {
"Thor",
"Cap",
"Tony Stark",
"Black Panther",
"Black Widow",
"Hulk",
"Spider-Man",
};
Random r = new Random();
binding.userName.setText(avengersNames[r.nextInt(avengersNames.length)]);
}
3. Add the following logic to the application:
- Use the following implementation for
onLogin
:
public void onLogin() {
VoxeetSDK.session().open(new ParticipantInfo(binding.userName.getText().toString(), "", ""))
.then((result, solver) -> {
Toast.makeText(MainActivity.this, "log in successful", Toast.LENGTH_SHORT).show();
updateViews();
})
.error(error());
}
- Use the following implementation for
onLogout
:
public void onLogout() {
VoxeetSDK.session().close()
.then((result, solver) -> {
Toast.makeText(MainActivity.this, "logout done", Toast.LENGTH_SHORT).show();
updateViews();
})
.error(error());
}
- Use the following implementation for
updateViews
:
private void updateViews() {
// Disable every view
setEnabled(views, false);
// If the user is not connected, we will only enabled the not logged in buttons
if (!VoxeetSDK.session().isOpen()) {
setEnabled(buttonsNotLoggedIn, true);
return;
}
}
Add a joining option
When event handlers are set, implement the join
and leave
conference function in the client.
1. To modify the layout, edit the main_activity.xml
file with the following content for Step 2:
<LinearLayout ...>
...
<!-- Step 2. Put the layout changes for the join conference step here -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="conference name :" />
<EditText
android:id="@+id/conference_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/join"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="start" />
<!-- Step 2.2. The layout will be upgraded in the leave conference step -->
</LinearLayout>
<!-- Step 3. ... -->
</LinearLayout>
2. Modify the interface linking in the MainActivity
class in MainActivity.java
:
- New method for
MainActivity
:
public void onJoin() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Add onClickListener for onJoin()
binding.join.setOnClickListener(_view -> onJoin());
- Add the following code to the
onCreate
method:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Add the join button and enable it only when not in a conference
add(views, R.id.join);
add(buttonsNotInConference, R.id.join);
// Set a default conference name
binding.conferenceName.setText("Avengers meeting");
}
3. Add the following logic to the application:
- Configure permission management in
MainActivity
onResume
. Simplify the permission flow to ask for microphone and camera permissions when the application resumes.
@Override
protected void onResume() {
...
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED
||
ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}, 0x20);
}
}
- Bind the join flow implementation in the
onJoin
method created previously. The method creates and joins the conference.
public void onJoin() {
ParamsHolder paramsHolder = new ParamsHolder();
paramsHolder.setDolbyVoice(true);
ConferenceCreateOptions conferenceCreateOptions = new ConferenceCreateOptions.Builder()
.setConferenceAlias(binding.conferenceName.getText().toString())
.setParamsHolder(paramsHolder)
.build();
VoxeetSDK.conference().create(conferenceCreateOptions)
.then((ThenPromise<Conference, Conference>) conference -> {
ConferenceJoinOptions conferenceJoinOptions = new ConferenceJoinOptions.Builder(conference)
.build();
return VoxeetSDK.conference().join(conferenceJoinOptions);
})
.then(conference -> {
Toast.makeText(MainActivity.this, "started...", Toast.LENGTH_SHORT).show();
updateViews();
})
.error((error_in) -> {
Toast.makeText(MainActivity.this, "Could not create conference", Toast.LENGTH_SHORT).show();
});
}
- In
updateViews
, enable and disable buttons based on the conference state.
private void updateViews() {
...
ConferenceInformation current = VoxeetSDK.conference().getCurrentConference();
// We can now add the logic to manage our basic state
if (null != current && VoxeetSDK.conference().isLive()) {
setEnabled(buttonsInConference, true);
} else {
setEnabled(buttonsNotInConference, true);
}
}
Add a leaving option
1. To modify the layout, edit the main_activity.xml
file, adding the following content for Step 2.2:
<LinearLayout ...>
...
<LinearLayout ...>
<!-- Step 2.2. The layout will be upgraded in the leave conference step -->
<Button
android:id="@+id/leave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="leave" />
</LinearLayout>
<!-- Step 3. ... -->
</LinearLayout>
2. Modify the interface linking in the MainActivity
class in MainActivity.java
:
- New method for
MainActivity
:
public void onLeave() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Add onClickListeners for onLeave()
binding.leave.setOnClickListener(_view -> onLeave());
- Add the following code to the
onCreate
method:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Add the leave button and enable it only while in a conference
add(views, R.id.leave);
add(buttonsInConference, R.id.leave);
}
3. Add the following logic to the application:
- Use the following implementation for
onLeave
:
public void onLeave() {
VoxeetSDK.conference().leave()
.then((result, solver) -> {
updateViews();
Toast.makeText(MainActivity.this, "left...", Toast.LENGTH_SHORT).show();
})
.error(error());
}
Run your application
On Android Studio, click on run
. Make sure that you have:
- a configured Android Virtual Device (AVD) available on your machine
- a connected Android Debug Bridge (ADB) ready to use with an Android device
Updated 4 months ago