Suggestions

close search

Add Messaging, Voice, and Authentication to your apps with Vonage Communications APIs

Visit the Vonage API Developer Portal

Back to Tutorials

Set up a Basic Android Client

This tutorial will walk you through the steps of setting up a basic Android client that uses the Vonage Video API.

Overview

All applications that use the Vonage Video API require both a client and a server component. The client-side code is what loads in an end-user’s device (phone, computer) and handles the majority of OpenTok functionality, including connecting to the session, publishing audio-video streams to the session, and subscribing to other clients’ streams. For more information on clients, servers, and sessions, see Video API Basics.

In this tutorial, you will be utilizing the OpenTok Android SDK, OpenTok's client-side library for Android devices, to quickly and easily build a real-time interactive video application.

Here are the items that will be covered in this tutorial:


Estimated completion time: 25 mins

Want to skip this tutorial? You can jump to the completed Android client code in the Basic-Video-Chat-Java folder of our Android sample app repo on GitHub. The repo includes a README with documentation for running and exploring the code. Also, this tutorial discusses the Java version of the Android sample app. For a Kotlin version with documentation, see the Android sample app repo.

Requirements

To complete this tutorial, you’ll need:

Step 1: Creating a new project

  1. Open Android Studio and select New Project from the File menu.

  2. Set the minimum SDK for the app to be API 16 (Android 4.1, Jelly Bean).

  3. Click through the wizard, ensuring that Empty Activity is selected. Leave the Activity Name set to MainActivity, and leave the Layout Name set to activity_main.

Step 2: Adding the OpenTok Android SDK

The app uses Maven to load the OpenTok Android SDK:

  1. Edit the build.gradle for your project (at your project's root) and add the following code snippet to the allprojects/repositories section:

    mavenCentral()
    

    At this point the build.gradle file should look like this:

    buildscript {
      repositories {
          google()
          jcenter()
      }
      dependencies {
          classpath 'com.android.tools.build:gradle:4.1.2'
      // NOTE: Do not place your application dependencies here; they belong
      // in the individual module build.gradle files
      }
    }
    
    allprojects {
       repositories {
           google()
           jcenter()
           mavenCentral()
       }
    }
    
    task clean(type: Delete) {
       delete rootProject.buildDir
    }
    
  2. Modify build.gradle for your module (the app/build.gradle file) and add the following code snippet to the dependencies section:

    implementation 'com.opentok.android:opentok-android-sdk:2.22.0'

    When using OpenTok Android 2.16.0 or above, you will need to add the following to to the `android` section of the app's build.gradle file as well:

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    

    At this point the build.gradle file for your module should look like this:

    apply plugin: 'com.android.application'
    
        android {
            compileSdkVersion 32
    
            defaultConfig {
                applicationId "com.example.myapplication"
                minSdkVersion 16
                targetSdkVersion 32
                versionCode 1
                versionName "1.0"
                testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
            }
            buildTypes {
                release {
                    minifyEnabled false
                    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                }
            }
        }
        
        dependencies {
            implementation fileTree(dir: 'libs', include: ['*.jar'])
            implementation 'androidx.appcompat:appcompat:1.0.2'
            implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
            implementation 'androidx.annotation:annotation:1.1.0'
            implementation 'com.opentok.android:opentok-android-sdk:2.22.2'
            testImplementation 'junit:junit:4.12'
            androidTestImplementation 'androidx.test:runner:1.2.0'
            androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
        }
    
  3. Sync your project.

  4. Step 3: Setting up authentication

    In order to connect to an OpenTok session, the client will need access to some authentication credentials — an API key, session ID, and token. In a production application, these credentials should be generated by a server, but to speed things up we will just hard code the values for now:

    1. Create the OpenTokConfig class (File | New | Java Class), add some static variables to store the API key, session ID, and token. We'll also add some constants for our log message identifier and permissions:

      public class OpenTokConfig {
          public static final String API_KEY = "";
          public static final String SESSION_ID = "";
          public static final String TOKEN = "";
      }
      
    2. Adjust the code by hard coding the values for the API_KEY, SESSION_ID and TOKEN.

      To do this, log into your Video API account, either create a new OpenTok API project or use an existing OpenTok API project, then go to your project page and scroll down to the Project Tools section. From there, you can generate a session ID and token manually. Use the project’s API key along with the session ID and token you generated.

    Important: You can continue to get the session ID and token values from your Account during testing and development, but before you go into production you must set up a server. We will discuss this in Next steps at the end of this tutorial.

    For more information on sessions, tokens, and servers, check out Video API Basics.

    Step 4: Requesting permissions

    Because our app uses audio and video from the user's device, we’ll need to add some code to request audio and video permissions. We'll use the EasyPermissions library to do this.

    1. Start by adding the EasyPermissions library to your project — open the build.gradle for your module (the app/build.gradle file) and add the following code snippet to the dependencies section:

      implementation 'pub.devrel:easypermissions:3.0.0'
    2. In your AndroidManifest.xml file, add this code snippet inside the manifest tags:

      <uses-permission android:name="android.permission.CAMERA" />
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.RECORD_AUDIO" />
      <uses-permission android:name="android.permission.WAKE_LOCK" />
      <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
      <uses-permission android:name="android.permission.READ_PHONE_STATE" />
      <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
      
      <uses-feature android:name="android.hardware.camera" />
      <uses-feature android:name="android.hardware.camera.autofocus" />
    3. In your MainActivity.java file, add a new method called onRequestPermissionsResult:

      @Override
      public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
      
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
          EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
      }

      This is boilerplate code to use the EasyPermissions library.

      Important: You will have to add imports manually, by clicking EasyPermissions (red text) and pressing the key combnation Option + Enter on macOS or Alt + Enter on Windows. This step may be required when pasting the code. You can also enable "Add unambigous imports on the fly" option (Preferences | Editor | Auto Import) to add imports automatically.

    4. Add the PERMISSIONS_REQUEST_CODE property at the top of the MainActivity.java file:

      private static final int PERMISSIONS_REQUEST_CODE = 124;
    5. Next we'll add a requestPermissions() method:

      @AfterPermissionGranted(PERMISSIONS_REQUEST_CODE)
      private void requestPermissions() {
          String[] perms = { Manifest.permission.INTERNET, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO };
          if (EasyPermissions.hasPermissions(this, perms)) {
              // initialize and connect to the session
      
          } else {
              EasyPermissions.requestPermissions(this, "This app needs access to your camera and mic to make video calls", PERMISSIONS_REQUEST_CODE, perms);
          }
      }

      This checks if the permissions have already been granted. If they haven’t, we prompt the user for camera and mic permissions with the `EasyPermissions.requestPermissions` method.

      Once permissions have been granted, this method will fire again due to the @AfterPermissionGranted(PERMISSIONS_REQUEST_CODE) annotation. We'll add some code to initialize the session and view objects in the coming steps.

    6. Now add requestPermission(); to call the method inside the onCreate() method:

      @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        
            requestPermissions();
        }

    Step 5: Connecting to the session

    Next, we will connect to the OpenTok session. You must do this before you can publish your audio-video stream to the session or view other participants streams.

    1. Add a session property to the MainActivity class (right after the last lines you added in Step 3):

      private Session session;
      

      The Session class is defined in the OpenTok Android SDK. It represents an OpenTok session and includes methods for interacting with the session.

    2. To facilicate logging, add a TAG property at the top of the MainActivity class:

      private static final String TAG = MainActivity.class.getSimpleName();
    3. In the requestPermissions() method you created in the last step, add the following lines of code under the // initialize and connect to the session comment to instantiate the Session object and call its connect(token) method:

      initializeSession(OpenTokConfig.API_KEY, OpenTokConfig.SESSION_ID, OpenTokConfig.TOKEN);
    4. Just below the requestPermissions() method, add the following lines of code:

      private void initializeSession(String apiKey, String sessionId, String token) {
          Log.i(TAG, "apiKey: " + apiKey);
          Log.i(TAG, "sessionId: " + sessionId);
          Log.i(TAG, "token: " + token);
      
          session = new Session.Builder(this, apiKey, sessionId).build();
          session.setSessionListener(sessionListener);
          session.connect(token);
      }

      This code uses the Session.Builder() to instantiate a Session object. The constructor takes three parameters:

      • The Android application context associated with this process
      • The OpenTok API key
      • The session ID

      The Session.Builder.build() method returns a new Session instance.

      The Session.setSessionListener() method sets the object that will implement the SessionListener interface. This interface includes callback methods are called in response to session-related events. (We will implement them in the next steps).

      The Session.connect() method of the session object connects the client application to the OpenTok session. You must connect before sending or receiving audio-video streams in the session (or before interacting with the session in any way). The connect() method takes one parameter: the authentication token for this client to connect to the OpenTok session.

    5. Next we will create the sessionListener property. Add the following code at the top of the MainActivity class:

      
      private Session.SessionListener sessionListener = new Session.SessionListener() {
          @Override
          public void onConnected(Session session) {
              Log.d(TAG, "onConnected: Connected to session: " + session.getSessionId());
          }
      
          @Override
          public void onDisconnected(Session session) {
              Log.d(TAG, "onDisconnected: Disconnected from session: " + session.getSessionId());
          }
      
          @Override
          public void onStreamReceived(Session session, Stream stream) {
              Log.d(TAG, "onStreamReceived: New Stream Received " + stream.getStreamId() + " in session: " + session.getSessionId());
          }
      
          @Override
          public void onStreamDropped(Session session, Stream stream) {
              Log.d(TAG, "onStreamDropped: Stream Dropped: " + stream.getStreamId() + " in session: " + session.getSessionId());
          }
      
          @Override
          public void onError(Session session, OpentokError opentokError) {
              Log.e(TAG, "Session error: " + opentokError.getMessage());
          }
      };
      
      • When the client connects to the OpenTok session, the implementation of the SessionListener.onConnected(session) method is called.

      • When the client disconnects from the OpenTok session, the implementation of the SessionListener.onDisconnected(session) method is called.

      • If the client fails to connect to the OpenTok session, the implementation of the SessionListener.onError(session, error) method is called.

      • When another client publishes a stream to the OpenTok session, the implementation of the SessionListener.onStreamReceived(session, stream) method is called.

      • When another client stops publishing a stream to the OpenTok session, the implementation of the SessionListener.onStreamDropped(session, stream) method is called.

      For now, the app prints to the debugger console when any of these events occur.

    6. Debug your application. If the app successfully connects to the OpenTok session, the SessionListener.onConnected(session) method logs to the debug console.

    7. Add the methods below to the MainActivity class to notify the session about Activity lifecycle events:

      @Override
      protected void onPause() {
          super.onPause();
      
          if (session != null) {
              session.onPause();
          }
      }
      
      @Override
      protected void onResume() {
          super.onResume();
      
          if (session != null) {
              session.onResume();
          }
      }

    Step 6: Adjusting the sample app UI

    The OpenTok Android SDK exposes videos you publish and subscribe to as View objects. You can add these as children of ViewGroup objects in your app. This sample app will use FrameLayout objects (which extend ViewGroup) as containers for the publisher and subscriber views:

    1. In Android Studio, open the app/res/layout/activity_main.xml file. Click the "Text" tab at the bottom of the editor to display the XML code for the file.

    2. Replace the file contents 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">
          
              <FrameLayout
                      android:layout_width="fill_parent"
                      android:layout_height="fill_parent"
                      app:layout_constraintBottom_toBottomOf="parent"
                      app:layout_constraintEnd_toEndOf="parent"
                      app:layout_constraintStart_toStartOf="parent"
                      app:layout_constraintTop_toTopOf="parent">
          
                  <FrameLayout
                          android:id="@+id/subscriber_container"
                          android:layout_width="fill_parent"
                          android:layout_height="fill_parent" />
          
                  <FrameLayout
                          android:id="@+id/publisher_container"
                          android:layout_width="90dp"
                          android:layout_height="120dp"
                          android:layout_gravity="bottom|end"
                          android:layout_marginEnd="16dp"
                          android:layout_marginRight="16dp"
                          android:layout_marginBottom="16dp"
                          android:background="#CCCCCC"
                          android:padding="2dp" />
          
              </FrameLayout>
          
          </androidx.constraintlayout.widget.ConstraintLayout>
      

      Be sure to replace the entire content of the <TextView> element (through the closing /> tag).

    3. Now declare publisherViewContainer and subscriberViewContainer as properties of the MainActivity class (right after the declaration for the session property):

      private FrameLayout publisherViewContainer;
      private FrameLayout subscriberViewContainer;
      
    4. Finally, in the existing onCreate() method, initialize these layout view objects by adding the following lines of code under the setContentView() method call:

      publisherViewContainer = findViewById(R.id.publisher_container);
      subscriberViewContainer = findViewById(R.id.subscriber_container);

      At this point, your onCreate() method should look like this:

      @Override
      protected void onCreate(Bundle savedInstanceState) {
      
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
      
          publisherViewContainer = findViewById(R.id.publisher_container);
          subscriberViewContainer = findViewById(R.id.subscriber_container);
      
          requestPermissions();
      }
      

    The steps above merely set up a sample layout for this app. Your own app can add publisher and subscriber views as children of other ViewGroup objects

    Step 7: Publishing a stream to the session

    When the app connects to the OpenTok session, we want it to publish an audio-video stream to the session, using the camera and microphone:

    1. Add a publisher property to the MainActivity class (after the declaration of the session property):

      private Publisher publisher;
      

      The Publisher class is defined in the OpenTok Android SDK.

    2. Modify the implementation of the SessionListener.onConnected() method to include code to publish a stream to the session:

      @Override
      public void onConnected(Session session) {
          Log.d(TAG, "onConnected: Connected to session: " + session.getSessionId());
      
          publisher = new Publisher.Builder(MainActivity.this).build();
          publisher.setPublisherListener(publisherListener);
          publisher.getRenderer().setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL);
          
          publisherViewContainer.addView(publisher.getView());
      
          if (publisher.getView() instanceof GLSurfaceView) {
              ((GLSurfaceView) publisher.getView()).setZOrderOnTop(true);
          }
      
          session.publish(publisher);
      }
      

      The code above uses Publisher.Builder() to instantiate a Publisher object. The constructor takes one parameter: the context associated with this process (although it may be different in a production app).

      The Publisher.setPublisherListener() method sets the object that will implement the PublisherListener interface. This interface includes callback methods are called in response to publisher-related events.

      Important: The context you used depends on the specific use case. However usually it is desired for the session to live outside of the Activity (for example, between activities). For production applications, it's convenient to use application context instead of activity context.

      The code then passes the Publisher object in as a parameter of the Session.publish() method. This method publishes an audio-video stream to the OpenTok session, using the camera and microphone of the Android device. (Note that in an Android virtual device, the OpenTok Android SDK uses a test video when publishing a stream).

      The Publisher object has a getView() returns, which returns an Android View object. This view displays the video captured from the device’s camera. The code adds this view as a subview of the publisherViewContainer object.

    3. To log events, add a publisherListener property to the sessionListener of the MainActivity:

      private PublisherKit.PublisherListener publisherListener = new PublisherKit.PublisherListener() {
          @Override
          public void onStreamCreated(PublisherKit publisherKit, Stream stream) {
              Log.d(TAG, "onStreamCreated: Publisher Stream Created. Own stream " + stream.getStreamId());
          }
      
          @Override
          public void onStreamDestroyed(PublisherKit publisherKit, Stream stream) {
              Log.d(TAG, "onStreamDestroyed: Publisher Stream Destroyed. Own stream " + stream.getStreamId());
          }
      
          @Override
          public void onError(PublisherKit publisherKit, OpentokError opentokError) {
              Log.e(TAG, "PublisherKit onError: " + opentokError.getMessage());
          }
      };
      

      Note that the Publisher class extends the PublisherKit class (also defined by the OpenTok Android SDK). The PublisherKit class is a base class you can use to create advanced publishers, (such as publishers that use custom video capturers or renderers).

    4. This implements the PublisherListener methods:

      • onStreamCreated(publisherKit, stream) — Called when the publisher starts streaming to the OpenTok session.

      • onStreamDestroyed(publisherKit, stream) — Called when the publisher stops streaming to the OpenTok session.

      • onError(error) — Called when the client fails in publishing to the OpenTok session. An OpentokError object is passed into the method.

    Step 8: Subscribing to other client streams

    We want clients to be able to subscribe to (or view) other clients’ streams in the session:

    1. Add a subscriber property to the MainActivity class (after the declaration of the publisher property):

      private Subscriber subscriber;
      

      The Subscriber class is defined in the OpenTok Android SDK. It defines an object that a client uses to subscribe to (view) a stream published by another client.

    2. Modify the implementation of the onStreamReceived(session, stream) method (one of the SessionListener callbacks) to include code to subscribe to other clients’ streams the session:

      @Override
          public void onStreamReceived(Session session, Stream stream) {
              Log.d(TAG, "onStreamReceived: New Stream Received " + stream.getStreamId() + " in session: " + session.getSessionId());
          
              if (subscriber == null) {
                  subscriber = new Subscriber.Builder(MainActivity.this, stream).build();
                  subscriber.getRenderer().setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL);
                  subscriber.setSubscriberListener(subscriberListener);
                  session.subscribe(subscriber);
                  subscriberViewContainer.addView(subscriber.getView());
              }
          }
          
    3. To log events Add subscriberListener property just below sessionListener of the MainActivity:

      SubscriberKit.SubscriberListener subscriberListener = new SubscriberKit.SubscriberListener() {
          @Override
          public void onConnected(SubscriberKit subscriberKit) {
              Log.d(TAG, "onConnected: Subscriber connected. Stream: " + subscriberKit.getStream().getStreamId());
          }
      
          @Override
          public void onDisconnected(SubscriberKit subscriberKit) {
              Log.d(TAG, "onDisconnected: Subscriber disconnected. Stream: " + subscriberKit.getStream().getStreamId());
          }
      
          @Override
          public void onError(SubscriberKit subscriberKit, OpentokError opentokError) {
              Log.e(TAG, "SubscriberKit onError: " + opentokError.getMessage());
          }
      };
      
    4. When another client publishes a stream to a session, this method is called, and a Stream object is passed in. The Stream class is defined in the OpenTok Android SDK, and it represents an audio-video stream in the OpenTok session.

      The code initializes an instance of the Subscriber class, defined in the OpenTok Android SDK. The Subscriber.Builder() constructor takes two parameters:

      • The Android application context associated with this process.

      • The Stream object (for the stream you want to view)

      The Session.subscribe(subscriber) method subscribes to the stream that was just received.

      subscriberViewContainer.addView(subscriber.getView()) places the new subscribed stream's view on the screen.

    5. Modify the implementation of the onStreamDropped(Session session, Stream stream) method (another one of the SessionListener callbacks):

      @Override
      public void onStreamDropped(Session session, Stream stream) {
          Log.i(TAG, "Stream Dropped");
      
          if (subscriber != null) {
              subscriber = null;
              subscriberViewContainer.removeAllViews();
          }
      }
      

      subscriberViewContainer.removeAllViews() removes a subscriber's view once the stream has dropped.

    6. Step 9: Running the app

      Now that your code is complete, you can run the app in the Android Studio emulator. This will create a simulated publisher video — since the emulator cannot access your webcam, the publisher video will display an animated graphic instead of your camera feed.

      To add a second publisher (which will display as a subscriber in your emulator), either run the app a second time in a connected Android device or use the OpenTok Playground to connect to the session in a supported web browser by following the steps below:

      1. Go to OpenTok Playground (must be logged into your Account)

      2. Select the Join existing session tab

      3. Copy the session ID you used in your MainActivity.java file and paste it in Session ID input field

      4. Click Join Session

      5. On the next screen, click Connect, then click Publish Stream

      6. You can adjust the Publisher options (not required), then click Continue to connect and begin publishing and subscribing

      At this point you should see the stream being published from your webcam as well as the stream being published by the emulator. Returning to the emulator, you should also see the new publisher displayed on the emulated screen.

      Congratulations! You've finished the 'Set up a Basic Android Client' tutorial.
      You can continue to play with and adjust the code you've developed here for the client-side of your application, but keep in mind that you'll need to implement the server component of your application before going to production (see Setting up your server below).

      You can view a completed version of this sample app in the Basic-Video-Chat-Java folder of the opentok-android-sdk-samples repo on GitHub. This completed version adds code to load the session ID, token, and API key from a web service (instead of using hard-coded values).

Setting up your server

    In the tutorial above, we had you hard code your authentication credentials. However, for a production application, the sessionId and token values in your code must be generated by your app server and passed to the client. Here are a couple of reasons why you don't want hard coded credentials in your production app:

    • Tokens expire after a certain amount of time (specified when generated), so new ones need to be generated regularly
    • You won't be able to create new sessions dynamically, so everyone using your app would be stuck in a single "room"

    You can continue testing your application with hard coded values, but when you're ready to set up a server there are a number of ways to do so:

    Server Option 1 — Launch a simple REST server on Heroku with one click

    This is probably the fastest way to get a server up and running, but it has limited functionality. Simply click the Heroku button below, at which point you'll be sent to Heroku's website and prompted for your OpenTok API Key and API Secret — you can get these values on your project page in your Video API account. If you don't have a Heroku account, you'll need to sign up (it's free).

    Deploy

    Want to explore the code? The button above launches server code from the learning-opentok-php GitHub repo. Visit the repo to review the code as well as additional documentation — you can even fork the repo and make changes before deploying.

    Prefer Node.js? Visit the learning-opentok-node repo for the same functionality using Node.js (including a Heroku deploy button).

    Once the server is deployed on Heroku, you'll need to add some code to your project to fetch the credentials from the server. Check client implementation in the Basic-Video-Chat-Java / Basic-Video-Chat-Kotlin project.

    Server Option 2 — Build from scratch using the server SDKs

    Option 1 uses REST endpoints for transmitting credentials to the client, but that's only one of many ways to implement a server with OpenTok. If you want a greater level of customization, you can review OpenTok's Server SDK documentation for the server-side language of your choice (available for PHP, Node.js, Java, .NET, Python and Ruby). The documentation goes over the setup process and the various methods you'll need to generate sessions and tokens, as well as other server-side functionality.