> For the complete documentation index, see [llms.txt](https://documentation.sceenic.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://documentation.sceenic.co/synchronization-sdk/tutorials/android/android-java-kotlin.md).

# Android - Java/Kotlin

## Overview <a href="#overview" id="overview"></a>

Follow this step-by-step tutorial to implement the video playback Synchronization SDK.

While the client-side SDK will take care of most of the functionality, in order to make this sample application work, you will need to use the API\_KEY provided to you.

Full code samples can be found here

* [Java sample application](https://bitbucket.org/svmt/samplesyncwatchtogether-java/src/master/)
* [Kotlin sample application](https://bitbucket.org/svmt/samplesyncwatchtogether-kotlin/src/master/)

## Requirements <a href="#requirements" id="requirements"></a>

To complete this guide successfully the following prerequisites are required:

* [Android Studio](https://developer.android.com/studio)
* A Sceenic account
* Access key for sync SDK

## Authentication <a href="#authentication" id="authentication"></a>

An `Access Token` is needed in order to allow a client to connect to a sync group.

{% hint style="danger" %}
**Note:** It is important that the client application does not request an Access Token directly from the backend. By doing that you risk exposing the API\_TOKEN and API\_SECRET.
{% endhint %}

### Acquiring an Access Token <a href="#acquiring-an-access-token" id="acquiring-an-access-token"></a>

{% tabs %}
{% tab title="cUrl(Bash)" %}

```bash
curl -iL --request GET --url https://YOUR_CAS_URL/sync/token --header 'auth-api-key: API_KEY'   --header 'auth-api-secret: API_SECRET'
```

{% endtab %}
{% endtabs %}

The `Access Token` is a JWT token - more about jwt you can read - [here](https://en.wikipedia.org/wiki/JSON_Web_Token).

A successful response will look like that:

```
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...."
}
```

{% hint style="info" %}
You can get your API\_KEY and API\_SECRET in your private area, [here](https://media.sceenic.co/).
{% endhint %}

{% hint style="warning" %}
**Note:** Every Sync Token corresponds to one specific sync group only. To allow two different clients to connect to the same group, the clients need to use the same `Access Token`.
{% endhint %}

## &#x20;Create a project <a href="#create-a-project" id="create-a-project"></a>

* Open the Android Studio and select **New Project** from the **File** menu
* Select **Empty Activity** and click next
* Choose Java or Kotlin as your programming language for the project
* Configure your project's location, application, and package names
* Set the minimum **SDK** version of the application to **21** or higher

## Add the sync SDK library to your project <a href="#add-the-sync-sdk-library-to-your-project" id="add-the-sync-sdk-library-to-your-project"></a>

* Create (if it does not exist) in the project a **libs** directory under the **app** folder
* Add the file **wtsyncsdk\_v....aar** to the **libs** folder
* Edit the application's **build.gradle** file (in the app folder) with SDK

{% tabs %}
{% tab title="Build.gradle" %}

```kotlin
android {
    // Required
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    
    // Required
    packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/ASL2.0'
        exclude("META-INF/*.kotlin_module")
    }
}
dependencies {	
    implementation files('libs/wtsyncsdk_v....aar')	
    implementation 'androidx.core:core-ktx:1.7.0'
}
```

{% endtab %}
{% endtabs %}

### Permissions <a href="#permissions" id="permissions"></a>

* Add the following permissions to the **AndroidManifest.xml** file under the **app/src/main** folder in the project above the application tag.

```kotlin
<uses-permission android:name = "android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name = "android.permission.INTERNET" />
```

## Instantiating the SDK <a href="#instantiating-the-sdk" id="instantiating-the-sdk"></a>

* SDK builder - Call SyncSdkBuilder to instantiate the SDK.

{% tabs %}
{% tab title="Java" %}

```java
mSyncSdk = new SyncSdk.SyncSdkBuilder()
      .accessToken(mAccessToken)      // mAccessToken - authorization token
      .name(mDisplayName)             // mDisplayName - username
      .syncListener(this)             // this - SyncListener object
      .build();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk = SyncSdk.SyncSdkBuilder()     
      .accessToken(mAccessToken)      // mAccessToken - authorization token
      .name(mDisplayName)             // mDisplayName - username
      .syncListener(this)             // this - SyncListener object
      .build() 
```

{% endtab %}
{% endtabs %}

* Start synchronization

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.startSync();
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.startSync()
```

{% endtab %}
{% endtabs %}

* Stop synchronization

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.stopSync();
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.stopSync()
```

{% endtab %}
{% endtabs %}

* Group play video

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.groupPlay();
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.groupPlay()
```

{% endtab %}
{% endtabs %}

* Group pause video

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.groupPause();
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.groupPause()
```

{% endtab %}
{% endtabs %}

* Group seek video

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.groupSeek(position);
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.groupSeek(position)
```

{% endtab %}
{% endtabs %}

* Group seek success

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.groupSeekSuccess(position);
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.groupSeekSuccess(position)
```

{% endtab %}
{% endtabs %}

## Managing synchronization logic <a href="#managing-synchronization-logic" id="managing-synchronization-logic"></a>

The synchronization logic is managed using callbacks, using them you will be able to create the interaction you need.

* In the sample application, you can see an example for implementing the necessary callbacks

{% tabs %}
{% tab title="Java" %}

```java
public class MainActivity extends AppCompatActivity implements SyncListener {
    ...

    @Override
    public void onClientList(@NotNull List<SyncClient> syncClients) {
        // remote sync client joined to the group, update ui using adapter
        // update ui with the list of connected clients in the same group
    }
    
    @Override
    public void onSetPlaybackRate(float rate) {
        // setting playback rate
    }

    @Override
    public void onPlaybackFromPosition(long position) {
       // playback from position
       // player.seekTo(position);
    }

    @Override
    public long onGetPlayerPosition() {
        // getting player position
        // Implement your logic to extract player position in miliseconds,
        // be sure that all clients connected to the same group extracts relevant
        // and from the same content timestamps(position)
        // return player.getCurrentPosition();
        return 0L;
    }

    @Override
    public float onGetPlaybackRate() {
        // getting playback rate
        // Implement your logic to extract player rate/speed
        // return player.speed;
        return 0f;
    }

    @Override
    public void onSyncInfo(@NotNull SyncInfo syncInfo) {
        // information about synchronization's accuracy and delta
        // syncInfo.accuracy 0.0% - 100.0%
        // syncInfo.delta starts from 0L and describes how big is delay
        // in miliseconds, can be negative value     
    }
    
    @Override
    public void onSyncGroupPlay() {
        // received an event onSyncGroupPlay()
        // play function should be called if its not playing already
        if (!player.isPlaying()) { 
            player.play();
        }   
    }
    
    @Override
    public void onSyncGroupPause() {
        // received an event onSyncGroupPause()
        // pause function should be called if its still playing
        if (player.isPlaying()) { 
            player.pause();
        }   
    }
    
    @Override
    public void onSyncGroupSeek(long position) {
        // received an event onSyncGroupSeek(long position) with position parameter
        // seek function should be called to the given position
        // works with VOD
        if (!player.isCurrentWindowLive()) { 
            player.seekTo(position);
        }
        // Client should send success seek request every time onSyncGroupSeek called
        if (mSyncSdk != null) {
            mSyncSdk.groupSeekSuccess(position);
        }
    }

    @Override
    public void onSyncDisconnected() {
        // client has been disconnected from the group. All connections 
        // will be closed. Clear ui and data
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
class MainActivity : AppCompatActivity(), SyncListener {
    ...

    override fun onClientList(syncClients: List<SyncClient>) {
        // remote client joined to the group, update ui using adapter
        // update ui with the list of connected clients in the same group
    }
    
    override fun onSetPlaybackRate(rate: Float) {
        // setting playback rate
    }
    
    override fun onPlaybackFromPosition(position: Long) {
       // playback from position
       // player.seekTo(position)
    }
    
    override fun onGetPlayerPosition(): Long {
        // getting player position
        // Implement your logic to extract player position in miliseconds,
        // be sure that all clients connected to the same group extracts relevant
        // and from the same content timestamps(position)
        // return player.getCurrentPosition()
        // Important! In case if content is Http Live Stream
        // Value of position shoulb be based on Program-Date-Time tag 
        return 0L
    }
    
     override fun onGetPlaybackRate(): Float {
        // getting playback rate
        // Implement your logic to extract player rate/speed
        // return player.speed
        return 0f
    }
    
    override fun onSyncInfo(syncInfo: SyncInfo)
         // information about synchronization's accuracy and delta
        // syncInfo.accuracy 0.0% - 100.0%
        // syncInfo.delta starts from 0L and describes how big is delay
        // in miliseconds, can be negative value   
    }
    
    
    override fun onSyncGroupPlay() {
        // received an event onSyncGroupPlay()
        // play function should be called
        player.play()
    }
        
    override fun onSyncGroupPause() {
        // received an event onSyncGroupPause()
        // pause function should be called
        player.pause()
    }
    
   override fun onSyncGroupSeek(position: Long) {
        // received an event onSyncGroupSeek(long position) with position parameter
        // seek function should be called to the given position
        // works with VOD
        if (!player.isCurrentWindowLive) { 
            player.seekTo(position)
        }
        // Client should send success seek request every time onSyncGroupSeek called
        mSyncSdk?.groupSeekSuccess(position)
    }
    
    override fun onSyncDisconnected() {
        // client has been disconnected from the group. All connections will be closed.
        // clear ui and data  
    }
```

{% endtab %}
{% endtabs %}

## Running the application <a href="#running-the-application" id="running-the-application"></a>

Once coding is finished, you should be able to run the application in the Android Studio emulator.

You can view the complete Watch Together sample application here

* [Java sample application](https://bitbucket.org/svmt/samplesyncwatchtogether-java/src/master/)
* [Kotlin sample application](https://bitbucket.org/svmt/samplesyncwatchtogether-kotlin/src/master/)

## Support <a href="#support" id="support"></a>

Need technical support? contact us at <Support@sceenic.co>.
