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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://documentation.sceenic.co/synchronization-sdk/tutorials/android/android-java-kotlin.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
