Suggestions

close search

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 OpenTok.

Overview

All OpenTok applications require both a client and a server component. The client-side code is what loads in an end-user’s Android device 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 OpenTok 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 folder of our Android sample app repo on GitHub. The repo includes a README with documentation for running and exploring the code.

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:

    maven {
       url  "http://tokbox.bintray.com/maven"
    }
    

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

    buildscript {
      repositories {
          jcenter()
      }
      dependencies {
          classpath 'com.android.tools.build:gradle:2.3.2'
      // NOTE: Do not place your application dependencies here; they belong
      // in the individual module build.gradle files
      }
    }
    
    allprojects {
       repositories {
           jcenter()
           maven {
               url  "http://tokbox.bintray.com/maven"
           }
       }
    }
    
    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:

    compile 'com.opentok.android:opentok-android-sdk:2.11.0'

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

    apply plugin: 'com.android.application'
    
    android {
       compileSdkVersion 25
       buildToolsVersion "25.0.2"
    
       defaultConfig {
           applicationId "com.example.yourname.myapplication"
           minSdkVersion 16
           targetSdkVersion 25
           versionCode 1
           versionName "1.0"
       }
       buildTypes {
           release {
               minifyEnabled false
           }
       }
    }
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:appcompat-v7:25.3.1'
        compile 'com.android.support.constraint:constraint-layout:1.0.2'
        testCompile 'junit:junit:4.12'
        compile 'com.opentok.android:opentok-android-sdk:2.11.0'
    }
    
    
  3. Sync your project.

  4. Add the OpenTok Android classes to the application — in Android Studio, open the MainActivity.java file. And add the following import statements:

    import android.util.Log;
    import com.opentok.android.Session;
    import com.opentok.android.Stream;
    import com.opentok.android.Publisher;
    import com.opentok.android.PublisherKit;
    import com.opentok.android.Subscriber;
    import com.opentok.android.OpentokError;
    import android.support.annotation.NonNull;
    import android.Manifest;
    import pub.devrel.easypermissions.AfterPermissionGranted;
    import pub.devrel.easypermissions.EasyPermissions;
    

    This imports the classes of the OpenTok Android SDK that the app will use. Don't worry about the compile errors for the easypermissions libraries. We'll fix those in a moment.

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. In the MainActivity 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 MainActivity extends AppCompatActivity {
      private static String API_KEY = "";
      private static String SESSION_ID = "";
      private static String TOKEN = "";
      private static final String LOG_TAG = MainActivity.class.getSimpleName();
      private static final int RC_SETTINGS_SCREEN_PERM = 123;
      private static final int RC_VIDEO_APP_PERM = 124;
    
  2. Adjust the code by hard coding the values for the API_KEY, SESSION_ID and TOKEN.

    To do this, log into your TokBox Account, either create a new project or use an existing 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 OpenTok 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:

    compile 'pub.devrel:easypermissions:0.4.0'

    This should resolve the compile errors from earlier. Make sure you sync your project before proceeding.

  2. 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.

  3. Next we'll add a requestPermissions method:

    @AfterPermissionGranted(RC_VIDEO_APP_PERM)
    private void requestPermissions() {
        String[] perms = { Manifest.permission.INTERNET, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO };
        if (EasyPermissions.hasPermissions(this, perms)) {
            // initialize view objects from your layout
    
    
            // initialize and connect to the session
    
    
        } else {
            EasyPermissions.requestPermissions(this, "This app needs access to your camera and mic to make video calls", RC_VIDEO_APP_PERM, 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(RC_VIDEO_APP_PERM) annotation. We'll add some code to initialize the session and view objects in the coming steps.

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 mSession;
    

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

  2. 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:

    mSession = new Session.Builder(this, API_KEY, SESSION_ID).build();
    mSession.setSessionListener(this);
    mSession.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 session ID
    • The token

    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.

  3. In the onCreate() method, add a line to call the requestPermission() method:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        requestPermissions();
    }
  4. Change the MainActivity class declaration to have it implement the Session.SessionListener interface:

    public class MainActivity extends AppCompatActivity
                            implements  Session.SessionListener {
    
  5. Next we will implement methods of the SessionListener interface. Add the following code to the end of the MainActivity class (before the closing bracket of the class):

    // SessionListener methods
    
    @Override
    public void onConnected(Session session) {
       Log.i(LOG_TAG, "Session Connected");
    }
    
    @Override
    public void onDisconnected(Session session) {
       Log.i(LOG_TAG, "Session Disconnected");
    }
    
    @Override
    public void onStreamReceived(Session session, Stream stream) {
       Log.i(LOG_TAG, "Stream Received");
    }
    
    @Override
    public void onStreamDropped(Session session, Stream stream) {
       Log.i(LOG_TAG, "Stream Dropped");
    }
    
    @Override
    public void onError(Session session, OpentokError opentokError) {
       Log.e(LOG_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.

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

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 <TextView> element with the following code:

    <FrameLayout
       android:layout_width="fill_parent"
       android:layout_height="fill_parent">
       <FrameLayout
           android:layout_width="fill_parent"
           android:layout_height="fill_parent"
           android:id="@+id/subscriber_container" />
       <FrameLayout
           android:layout_width="90dp"
           android:layout_height="120dp"
           android:id="@+id/publisher_container"
           android:layout_gravity="bottom|end"
           android:layout_marginBottom="16dp"
           android:layout_marginEnd="16dp"
           android:layout_marginRight="16dp"
           android:padding="2dp"
           android:background="#FFFFFF" />
    </FrameLayout>
    

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

  3. Now open the MainActivity.java file in the Android Studio editor, and add the FrameLayout class in an import statement:

    import android.widget.FrameLayout;
    
  4. Then declare mPublisherViewContainer and mSubscriberViewContainer as properties of the MainActivity class (right after the declaration for the mSession property):

    private FrameLayout mPublisherViewContainer;
    private FrameLayout mSubscriberViewContainer;
    
  5. Finally, in the existing requestPermissions() method, initialize these layout view objects by adding the following lines of code under the // initialize view objects from your layout comment:

    mPublisherViewContainer = (FrameLayout)findViewById(R.id.publisher_container);
    mSubscriberViewContainer = (FrameLayout)findViewById(R.id.subscriber_container);

    At this point your requestPermissions method should look like this:

    
    @AfterPermissionGranted(RC_VIDEO_APP_PERM)
    private void requestPermissions() {
        String[] perms = { Manifest.permission.INTERNET, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO };
        if (EasyPermissions.hasPermissions(this, perms)) {
            // initialize view objects from your layout
            mPublisherViewContainer = (FrameLayout) findViewById(R.id.publisher_container);
            mSubscriberViewContainer = (FrameLayout) findViewById(R.id.subscriber_container);
    
            // initialize and connect to the session
            mSession = new Session.Builder(this, API_KEY, SESSION_ID).build();
            mSession.setSessionListener(this);
            mSession.connect(TOKEN);
    
        } else {
            EasyPermissions.requestPermissions(this, "This app needs access to your camera and mic to make video calls", RC_VIDEO_APP_PERM, perms);
        }
    }

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 an mPublisher property to the MainActivity class (after the declaration of the mSession property):

    private Publisher mPublisher;
    

    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.i(LOG_TAG, "Session Connected");
    
       mPublisher = new Publisher.Builder(this).build();
       mPublisher.setPublisherListener(this);
    
       mPublisherViewContainer.addView(mPublisher.getView());
       mSession.publish(mPublisher);
    }
    

    The code above uses Publisher.Builder() to instantiate a Publisher object. The constructor takes one parameter: the Android application context associated with this process.

    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. (We will implement them in the next step).

    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 mPublisherViewContainer object.

  3. Change the MainActivity class declaration to have it implement the PublisherKit.PublisherListener interface:

    public class MainActivity extends AppCompatActivity
                             implements  Session.SessionListener,
                             PublisherKit.PublisherListener {
    

    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. Add the following code to the end of the MainActivity class (before the closing bracket of the class):

    // PublisherListener methods
    
    @Override
    public void onStreamCreated(PublisherKit publisherKit, Stream stream) {
        Log.i(LOG_TAG, "Publisher onStreamCreated");
    }
    
    @Override
    public void onStreamDestroyed(PublisherKit publisherKit, Stream stream) {
        Log.i(LOG_TAG, "Publisher onStreamDestroyed");
    }
    
    @Override
    public void onError(PublisherKit publisherKit, OpentokError opentokError) {
       Log.e(LOG_TAG, "Publisher error: " + opentokError.getMessage());
    }
    

    This implements the PublisherListener methods:

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

    • onStreamCreated(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.

Debug your application. If the app successfully connects to the OpenTok session, it will publish a stream to the session, and you will see the publisher’s video in the app.

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 mSubscriber property to the MainActivity class (after the declaration of the mPublisher property):

    private Subscriber mSubscriber;
    

    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.i(LOG_TAG, "Stream Received");
    
        if (mSubscriber == null) {
            mSubscriber = new Subscriber.Builder(this, stream).build();
            mSession.subscribe(mSubscriber);
            mSubscriberViewContainer.addView(mSubscriber.getView());
        }
    }
    

    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.

    mSubscriberViewContainer.addView(mSubscriber.getView()) places the new subscribed stream's view on the screen.

  3. 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(LOG_TAG, "Stream Dropped");
    
        if (mSubscriber != null) {
            mSubscriber = null;
            mSubscriberViewContainer.removeAllViews();
        }
    }
    

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

  4. 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 (Chrome, Firefox, or Internet Explorer 10-11) 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 ViewController.m 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 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:

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 TokBox 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:

  1. In your MainActivity.java file, add the following import statements (under import android.widget.FrameLayout;):

    import com.android.volley.Request;
    import com.android.volley.RequestQueue;
    import com.android.volley.Response;
    import com.android.volley.VolleyError;
    import com.android.volley.toolbox.JsonObjectRequest;
    import com.android.volley.toolbox.Volley;
    
    import org.json.JSONException;
    import org.json.JSONObject;
  2. In the same MainActivity.java file, add the following method (under private FrameLayout mSubscriberViewContainer;):

    public void fetchSessionConnectionData() {
            RequestQueue reqQueue = Volley.newRequestQueue(this);
            reqQueue.add(new JsonObjectRequest(Request.Method.GET,
                    "https://YOURAPPNAME.herokuapp.com" + "/session",
                    null, new Response.Listener<JSONObject>() {
    
                @Override
                public void onResponse(JSONObject response) {
                    try {
                        API_KEY = response.getString("apiKey");
                        SESSION_ID = response.getString("sessionId");
                        TOKEN = response.getString("token");
    
                        Log.i(LOG_TAG, "API_KEY: " + API_KEY);
                        Log.i(LOG_TAG, "SESSION_ID: " + SESSION_ID);
                        Log.i(LOG_TAG, "TOKEN: " + TOKEN);
    
                        mSession = new Session.Builder(MainActivity.this, API_KEY, SESSION_ID).build();
                        mSession.setSessionListener(MainActivity.this);
                        mSession.connect(TOKEN);
    
                    } catch (JSONException error) {
                        Log.e(LOG_TAG, "Web Service error: " + error.getMessage());
                    }
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e(LOG_TAG, "Web Service error: " + error.getMessage());
                }
            }));
        }

    The fetchSessionConnectionData() method fetches session information form the /session endpoint of the server you just set up. Once the data is returned, the variables you previously hard coded, Api Key, Session ID, and Token, are set programmatically. The /session endpoint will always return the same session ID, but will produce a new token each time it's called — this results in each client receiving a unique token.

    You'll need to replace https://YOURAPPNAME.herokuapp.com with your actual Heroku app url — you can find this on your app page on the Heroku website.

  3. In the same file, replace the existing requestPermissions method with the following:

    @AfterPermissionGranted(RC_VIDEO_APP_PERM)
            private void requestPermissions() {
                String[] perms = { Manifest.permission.INTERNET, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO };
                if (EasyPermissions.hasPermissions(this, perms)) {
                    // initialize view objects from your layout
                    mPublisherViewContainer = (FrameLayout) findViewById(R.id.publisher_container);
                    mSubscriberViewContainer = (FrameLayout) findViewById(R.id.subscriber_container);
    
                    // initialize and connect to the session
                    fetchSessionConnectionData();
    
                } else {
                    EasyPermissions.requestPermissions(this, "This app needs access to your camera and mic to make video calls", RC_VIDEO_APP_PERM, perms);
                }
            }

    This updates the method to use the credentials returned from fetchSessionConnectionData() instead of your hard coded values.

  4. Finally, in the build.gradle for your module (the app/build.gradle file), add the following line to your dependencies:

    compile 'com.android.volley:volley:1.0.0'

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.