Home

Awesome

Accelerator Core Android

Build Status GitHub release license MIT

<img src="https://assets.tokbox.com/img/vonage/Vonage_VideoAPI_black.svg" height="48px" alt="Tokbox is now known as Vonage" />

Quickstart

The Accelerator Core Android library gives you an easy way to integrate OpenTok SDK to any Android applications.

Installation

Using the repository

  1. Clone the accelerator core android repository.
  2. Start Android Studio and create a project.
  3. From the project, right-click the app name and select > Module > Import Gradle Project.
  4. Navigate to the directory in which you cloned OpenTok Accelerator Core Android, select accelerator-core, and click Finish.
  5. Open the build.gradle file for the app and ensure the following lines have been added to the dependencies section:
implementation project(':accelerator-core-android')

Using Maven

Download it from http://tokbox.bintray.com/maven. For example:

a) Edit the build.gradle for your project and add the following code snippet to the all projects/repositories section:

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

b) Modify build.gradle for your module and add the following code snippet to the dependencies section:

implementation 'com.opentok.android:opentok-accelerator-core:x.y.z’

Accelerator Core

Exploring the Code

For detail about the APIs used to develop this library, see the OpenTok Android SDK Reference and Android API Reference.

ClassDescription
OTAcceleratorSessionManages the session, allows multiple listeners and implements a signaling protocol.
OTWrapperRepresents an OpenTok object to enable a video communication.
OTConfigDefines the OpenTok Configuration to be used in the communication. It includes SessionId, Token and APIKey, and features like to subscribe automatically or subscribe to self.
PreviewConfigDefines the configuration of the local preview.
BasicListenerMonitors basic state changes in the OpenTok communication.
AdvancedListenerMonitors advanced state changes in the OpenTok communication.
SignalListenerMonitors a signal received in the OpenTok communication.
MediaTypeDefines the Audio and Video media type.
StreamStatusDefines the current status of the Stream properties.
VideoScaleDefines the FIT and FILL modes setting for the renderer.
ScreenSharingCapturerCustom screen sharing capturer. In the absence of a custom or default camera capturer, Core Android will generate one using the full screen as the capturer.
ScreenSharingFragmentHeadless fragment used to implement the screensharing feature by default.

Using Accelerator Core Android

You can start testing a basic multiparty application using the Accelerator Core with best-practices for Android.

Initialization

The first step in using the OTWrapper is to initialize it by calling the constructor with the OpenTokConfig parameter.

OTConfig config = OTConfig.OTConfigBuilder(SESSION_ID, TOKEN, API_KEY)
  .name("core-sample")
  .subscribeAutomatically(true)
  .subscribeToSelf(false)
  .build();

OTWrapper wrapper = OTWrapper(this, config);

Obtaining OpenTok Credentials

To use OpenTok's framework you need a Session ID, Token, and API Key you can get these values at the OpenTok Developer Dashboard. For production deployment, you must generate the Session ID and Token values using one of the OpenTok Server SDKs.

Set and define listeners


wrapper.addBasicListener(mBasicListener);
wrapper.addAdvancedListener(mAdvancedListener);

....
val basicListener = PausableBasicListener<OTWrapper>(object : BasicListener<OTWrapper> {
  //...
});

val advancedListener = PausableAdvancedListener<OTWrapper>(object : AdvancedListener<OTWrapper> {
  //...
});

Connect and disconnect from an OpenTok session

Call to connect or disconnect from an OpenTok session. When the OTWrapper is connected, the BasicListener.onConnected(...) event is called. If the OTWrapper failed to connect, the BasicListener.onError(...) event is called.


wrapper.connect();

//.....

wrapper.disconnect();

Each time a participant connects to the same session, the BasicListener.onConnected(...) event is called. This event offers the information about the connection id of the participant who connected, the total connections count in the session and the data of the connection.

To check if the connection is our own connection or not, use OTWrapper.getOwnConnId().

  val isConnected: Boolean = false;
  val remoteConnId: String;

  val basicListener = PausableBasicListener<OTWrapper>(object : BasicListener<OTWrapper> {

      override fun onConnected(otWrapper: OTWrapper, participantsCount: Int, connId: String, data: String) {
          Log.i(LOG_TAG, "Connected to the session. Number of participants: $participantsCount, connId: $connId")

          if (wrapper.ownConnId == connId) {
              isConnected = true;
          }
          else {
              remoteConnId = connId;
          }
      }
    //....
});

Start and stop preview

Call to start and stop displaying the camera's video in the Preview's view before it starts streaming video. Therefore, the other participants are not going to receive this video stream.

val previewConfig = PreviewConfig.PreviewConfigBuilder()
                        .name("Tokboxer")
                        .build()

wrapper.startPreview(previewConfig);
//.....
wrapper.stopPreview();

When the OTWrapper started the preview, the BasicListener.onPreviewViewReady(...) event is called. And when the OTWrapper stopped the preview, the BasicListener.onPreviewViewDestroyed(...) event is called.

val basicListener = PausableBasicListener<OTWrapper>(object : BasicListener<OTWrapper> {

    override fun onPreviewViewReady(otWrapper: OTWrapper, localView: View) {
      Log.i(LOG_TAG, "Local preview view is ready")
    }
    override fun onPreviewViewDestroyed(otWrapper: OTWrapper) {
      Log.i(LOG_TAG, "Local preview view is destroyed")
    }
});

Start and stop publishing media

Call to start and stop the local streaming video. The source of the stream can be the camera or the screen. To indicate the screen source, it is necessary to set the screen-sharing parameter to TRUE.

val config: PreviewConfig;

//camera streaming
config = PreviewConfig.PreviewConfigBuilder()
                      .name("Tokboxer")
                      .build();

wrapper.startPublishingMedia(config, false);

//or screen streaming, using a custom screen capturer
config = PreviewConfig.PreviewConfigBuilder().
                  name("screenPublisher")
                  .capturer(screenCapturer)
                  .build();

wrapper.startSharingMedia(config, true);

When the OTWrapper started the publishing media, the BasicListener.onStartedPublishingMedia(...) event is called. And when the OTWrapper stopped the publishing media, the BasicListener.onStoppedPublishingMedia(...) event is called.

val basicListener = PausableBasicListener<OTWrapper>(object : BasicListener<OTWrapper> {

  override fun onStartedPublishingMedia(otWrapper: OTWrapper, screenSharing: Boolean) {
    Log.i(LOG_TAG, "Local started streaming video.")
  }

  override fun onStoppedPublishingMedia(otWrapper: OTWrapper, screenSharing: Boolean) {
    Log.i(LOG_TAG, "Local stopped streaming video.")
  }
});

Remote participants management

To subscribe automatically to a participant connected to the session, the subscribeAutomatically property in the OTConfig has to be TRUE. Then, when a remote participant connected to the session, the BasicListener.onRemoteJoined(...) event is called. And the BasicListener.onRemoteLeft(...) event is called. These callbacks contain the identifier for the remote participant, which is equals to the stream id of them.

val basicListener = PausableBasicListener<OTWrapper>(object : BasicListener<OTWrapper> {
   override fun onRemoteJoined(otWrapper: OTWrapper, remoteId: String) {
      Log.i(LOG_TAG, "A remote joined.")
    }

    override fun onRemoteLeft(otWrapper: OTWrapper, remoteId: String) {
      Log.i(LOG_TAG, "A remote left.")
    }
});

When the remote participant view is ready, the BasicListener.onRemoteViewReady(...) event is called. And when the remote participant view is destroyed, the BasicListener.onRemoteViewDestroyed(....) event is called.

val basicListener = PausableBasicListener<OTWrapper>(object : BasicListener<OTWrapper> {

  override fun onRemoteViewReady(otWrapper: OTWrapper, remoteView: View, remoteId: String, data: String) {
    Log.i(LOG_TAG, "Participant remote view is ready")
  }

  override fun onPreviewViewDestroyed(otWrapper: OTWrapper) {
    Log.i(LOG_TAG, "Local preview view is destroyed")
  });

Pause and resume communication

Call these methods when the app's activity pauses or resumes. These pause or resume the video for the local preview and remotes. The Accelerator Core offers the possibility to resume the events setting the resumeEvents parameter to TRUE in the resume method.

wrapper.pause();

//.....

wrapper.resume(true);  

Connections management

The Accelerator Core offers a set of methods to manage the connections of the session.

//get our own connection Id
wrapper.ownConnId

//get the total connections in the session
wrapper.connectionsCount

//check if the own connection is the oldest in the current session
wrapper.isTheOldestConnection;

//compare the connections creation times between the local connection and the argument passing
int older = wrapper.compareConnectionsTimes(remoteConnId);

Enable and disable the publishing and receiving media

To enable or disable the publishing audio or video.

//check the current status of the publishing video
wrapper.isPublishingMediaEnabled(MediaType.Video);

//check the current status of the publishing audio
wrapper.isPublishingMediaEnabled(MediaType.Audio);

//enable the video
wrapper.enablePublishingMedia(MediaType.Video, true);

//disable the audio
wrapper.enablePublishingMedia(MediaType.Audio, false);

Get stream status

The status of a stream includes the media status, the stream type, the status of the media containers and the stream dimensions.

//to get the publishing stream status
wrapper.getPublishingStreamStatus();

//to get the remote stream status
wrapper.getRemoteStremStatus(remoteId);

Signals Management

The Accelerator Core includes a complete Signaling protocol to register a signal listener for a given type.

wrapper.addSignalListener(SIGNAL_TYPE, this);

//send a signal to all the participants
wrapper.sendSignal(SignalInfo(wrapper.ownConnId, null, SIGNAL_TYPE, "hello"));

//send a signal to a specific participant, using the participant connection id.
wrapper.sendSignal(SignalInfo(wrapper.ownConnId, participantConnId, SIGNAL_TYPE, "hello"));

//manage the received signals. All the received signals will be of the registered type: SIGNAL_TYPE
public void onSignalReceived(signalInfo: SignalInfo, isSelfSignal: Boolean) { }

Customize capturer and renderers

A custom video capturer or renderer can be used in the OpenTok communication for the publishing media.

val myCustomRenderer = CustomRenderer(...);
val myCustomCapturer = CustomCapturer(...);

val config = PreviewConfig.PreviewConfigBuilder().
                  name("screenPublisher").capturer(myCustomCapturer).renderer(myCustomRenderer).build();

wrapper.startPublishingMedia(config, false);

A custom video renderer can be used in the OpenTok communication for the received media. Please note, this should be set before to start the communication.

val myCustomRenderer = CustomRenderer(...);

//set a custom renderer dor the received video stream
wrapper.setRemoteVideoRenderer(myCustomRenderer);

//or set a custom renderer for the received screen stream
wrapper.setRemoteScreenRenderer(myCustomRenderer);

Set Video Renderer styles

The video scale mode can be modified to FILL or FIT value for the publishing video or for the received video from the remotes.

wrapper.setPublishingStyle(VideoScalse.FIT);
wrapper.setRemoteStyle(remoteId, VideoScale.FILL);

Cycle the camera

Cycle between cameras, if there are multiple cameras on the device. Then, the AdvancedListener.onCameraChanged(...) event is called.

wrapper.cycleCamera();

Screen-sharing

According to start and stop publishing media, you can start screensharing using OTWrapper

wrapper.startPublishingMedia(PreviewConfig.PreviewConfigBuilder()
                       .name("Tokboxer")
                       .build(), true);

Scree-sharing with a customer capturer can be achieved using:

val previewConfig = PreviewConfig.PreviewConfigBuilder()
                       .name("Tokboxer")
                       .capturer(myCapturer)
                       .build()

wrapper.startPublishingMedia(previewConfig, true);

Using OTAcceleratorSession

The Accelerator Core library uses the OTAcceleratorSession to manage the OpenTok Session. This class lets you have several Session listeners and manage the signalling in the lib.

In the case, you don't need the audio/video communication, you can start by creating an OTAcceleratorSession instance, an apiKey and sessionID are requires. For more visit Obtaining OpenTok Credentials

OTAcceleratorSession session = OTAcceleratorSession(context, apikey, sessionId);
session.addSignalListener("CHAT", this);

//send a signal
val messageObj = JSONObject();
messageObj.put("sender", "Tokbox");
messageObj.put("text", "hi!");
messageObj.put("sentOn", System.currentTimeMillis());

session.sendSignal(SignalInfo(mSession.getConnection().getConnectionId(), null, "CHAT", messageObj.toString()), null);

To get the OTAcceleratorSession instance used in the audio/video communication, call to:

OTAcceleratorSession session = wrapper.session;

Accelerator TextChat

screenshot

Exploring the code

The TextChatFragment class is the backbone of the text chat communication feature.

This class, which inherits from the androidx.fragment.app.Fragment class, sets up the text chat UI views and events, sets up session listeners, and defines a listener interface to monitor the changes.

public class TextChatFragment extends Fragment implements SignalListener {
    . . .
}

The TextChatListener interface monitors state changes in the TextChatFragment, and defines the following methods:

public interface TextChatListener {
  void onNewSentMessage(ChatMessage message);
  void onNewReceivedMessage(ChatMessage message);
  void onTextChatError(String error);
  void onClose();
}

### Initialization methods

The following `TextChatFragment` methods are used to initialize the app and provide basic information determining the behavior of the text chat functionality.

| Feature                                           | Methods              |
| ------------------------------------------------- | -------------------- |
| Set the maximum chat text length.                 | `setMaxTextLength()` |
| Set the sender alias of the outgoing messages.    | `setSenderAlias()`   |
| Set the listener object to monitor state changes. | `setListener()`      |


For example, the following private method instantiates a `TextChatFragment` object:

```java
  OTAcceleratorSession mSession = new OTAcceleratorSession (context, apikey, sessionId);
  mSession.connect(token);

  //...

  private void initTextChatFragment() {
    mTextChatFragment = TextChatFragment.newInstance(mSession, apikey);
    getSupportFragmentManager().beginTransaction()
                .add(R.id.textchat_fragment_container, mTextChatFragment).commit();
    getSupportFragmentManager().executePendingTransactions();    
  }

This lines of code illustrates how to set the maximum message length to 1050 characters and set a new sender alias:

  try {
    mTextChatFragment.setSenderAlias("Tokboxer");
    mTextChatFragment.setMaxTextLength(140);
    mTextChatFragment.setListener(this);
  } catch (Exception e) {
    e.printStackTrace();
  }

Sending and receiving messages

By implementing the TextChatFragment.TextChatListener interface, the app can monitor both receiving and sending activity. For example, a message is successfully sent, or a message is sent with a code in the event of an error.

The method implementations shown below use the ChatMessage object to send and receive messages.

The onNewSentMessage() event is fired when a new individual ChatMessage is sent to other client connected to the OpenTok session. To send a ChatMessage, the TextChatFragment uses the OpenTok signaling API.

The onNewReceivedMessage() event is fired when a new ChatMessage is received from the other client.

    @Override
    public void onNewSentMessage(ChatMessage message) {
        Log.i(LOG_TAG, "New sent message");
    }

    @Override
    public void onNewReceivedMessage(ChatMessage message) {
        Log.i(LOG_TAG, "New received message");
    }

Accelerator Annotations

screenshot

Exploring the code

For detail about the APIs used to develop this accelerator pack, see the OpenTok Android SDK Reference and Android API Reference.

NOTE: The project contains logic used for logging. This is used to submit anonymous usage data for internal TokBox purposes only. We request that you do not modify or remove any logging code in your use of this accelerator pack.

Class design

ClassDescription
AnnotationsToolbarProvides the initializers and methods for the annotation toolbar view, and initializes functionality such as text annotations, a screen capture button, an erase button that removes the last annotation that was added, a color selector for drawing strokes and text annotations, and controls scrolling. You can customize this toolbar.
AnnotationsViewProvides the rectangular area on the screen which is responsible for drawing annotations and event handling.
AnnotationsListenerMonitors state changes in the Annotations component. For example, a new event would occur when a screen capture is ready or there is an error.
AnnotationsPathExtends the Android Path class, and defines the various geometric paths to be drawn in the AnnotationView canvas.
AnnotationTextDefines the text labels to be drawn in the AnnotationViewCanvas.
AnnotatableEach AnnotationText or AnnotationPath is defined as an annotatable object.
AnnotationsManagerManages the set of the annotations in the annotations view.
AnnotationsVideoRendererExtends the BaseVideoRenderer class in the OpenTok Android SDK, and includes screenshot functionality.

NOTE: Scrolling is frozen while the user adds annotations. Scrolling is re-enabled after the user clicks Done, and the annotations are removed at that point.

Using the Accelerator Annotation

Add the annotation toolbar

Add the AnnotationsToolbar to your layout:</p>

<com.tokbox.android.annotations.AnnotationsToolbar
    android:id="@+id/annotations_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom"/>

The AnnotationsToolbar offers the following actions:

Add a custom annotation renderer

If you would like to create a new instance of the AnnotationsVideoRenderer class or a new custom video renderer, for example, to manage the screen capture option in the annotations toolbar, start with this line of code:

AnnotationsVideoRenderer mRenderer = new AnnotationsVideoRenderer(this);

Attach the annotation canvas to a view

You can attach an annotation canvas to a publisher view, for example, to the screen sharing view:

try {
  AnnotationsView mScreenAnnotationsView = new AnnotationsView(this, mWrapper.getSession(), OpenTokConfig.API_KEY, true);

  mScreenAnnotationsView.attachToolbar(mAnnotationsToolbar);
  mScreenAnnotationsView.setVideoRenderer(mScreensharingRenderer);
  mScreenAnnotationsView.setAnnotationsListener(this);
  ((ViewGroup) mScreenSharingView).addView(mScreenAnnotationsView);
} catch (Exception e) {
  Log.i(LOG_TAG, "Exception - add annotations view " + e);
}

Or to a subscriber view:

try {
  AnnotationsView mRemoteAnnotationsView = new AnnotationsView(this, mWrapper.getSession(), OpenTokConfig.API_KEY, mRemoteConnId);
  mRemoteAnnotationsView.setVideoRenderer(mRemoteRenderer);
  mRemoteAnnotationsView.attachToolbar(mAnnotationsToolbar);
  mRemoteAnnotationsView.setAnnotationsListener(this);
  ((ViewGroup) mRemoteViewContainer).addView(mRemoteAnnotationsView);
} catch (Exception e) {
  Log.i(LOG_TAG, "Exception - add annotations view " + e);
}

Implement an annotations listener class

To listen for annotation events, implement an AnnotationsListener:

public  interface AnnotationsListener {
  void onScreencaptureReady(Bitmap bmp);
  void onAnnotationsSelected(AnnotationsView.Mode mode);
  void onAnnotationsDone();
  void onError(String error);
}
public class MainActivity
    extends AppCompatActivity
    implements AnnotationsView.AnnotationsListener {

    @Override
    public void onScreencaptureReady(Bitmap bmp) {
        //A new screencapture is ready
    }

    @Override
    public void onAnnotationsSelected(AnnotationsView.Mode mode) {
        //An annotations item in the toolbar is selected
    }

    @Override
    public void onAnnotationsDone() {
        //The DONE button annotations item in the toolbar is selected. Scrolling is re-enabled.
    }

    @Override
    public void onError(String error) {
       //An error happens in the annotations
    }
  ...
}

Development and Contributing

Interested in contributing? We :heart: pull requests! See the Contribution guidelines.

Getting Help

We love to hear from you so if you have questions, comments or find a bug in the project, let us know! You can either:

Further Reading