# Android Integration

## Environment Setup

To get started with Android Connector SDK, the requirement is an [Android Studio](https://developer.android.com/studio/) & [Android SDK](https://ionicframework.com/docs/reference/glossary#android-sdk). Android Studio is the IDE for creating native Android apps. It includes the [Android SDK](https://ionicframework.com/docs/reference/glossary#android-sdk), which will need to be configured for use in the command line. \
Android Studio is also used to [create Android virtual devices](https://ionicframework.com/docs/developing/android#creating-an-android-virtual-device), which are required for the Android emulator.

### Installing Android Studio[​](https://ionicframework.com/docs/developing/android#installing-android-studio) <a href="#installing-android-studio" id="installing-android-studio"></a>

Download Android Studio from the [Android website](https://developer.android.com/studio/). More detailed installation instructions can be found in the [User Guide](https://developer.android.com/studio/install).

### Installing the Android SDK[​](https://ionicframework.com/docs/developing/android#installing-the-android-sdk)

Once installed, open Android Studio. The IDE should detect that the Android SDK needs to be installed. In the **SDK Components Setup** screen, finish installing the SDK. Keep note of the **Android SDK Location**.

<figure><img src="/files/W2sbFw9M6JYxd61wzH7u" alt=""><figcaption></figcaption></figure>

By default, the latest stable SDK Platform is installed, which includes a collection of packages required to target that version of Android.

To install system images and other minor SDK platform packages, you may need to ensure **Show Package Details** is checked at the bottom of the SDK Manager.

<figure><img src="/files/LbjBd9YySbR6fOJDuK2b" alt=""><figcaption></figcaption></figure>

For future reference, the Android SDK can be managed with Android Studio in the **Configure** » **SDK Manager** menu of the Android Studio welcome screen or **Tools** » **SDK Manager** inside Android projects.

### Configuring Command Line Tools[​](https://ionicframework.com/docs/developing/android#configuring-command-line-tools) <a href="#configuring-command-line-tools" id="configuring-command-line-tools"></a>

The Android SDK ships with [useful command-line tools](https://developer.android.com/studio/command-line/). Before they can be used, some environment variables must be set. The following instructions are for macOS and Linux. For Windows, check the documentation on setting and persisting environment variables in terminal sessions.

In `~/.bashrc`, `~/.bash_profile`, or similar shell startup scripts, make the following modifications:

1. Set the `ANDROID_SDK_ROOT` environment variable. This path should be the **Android SDK Location** used in the previous section.

   For Mac:

   ```
   $ export ANDROID_SDK_ROOT=$HOME/Android/sdk
   ```

   For Linux/Windows:

   ```
   $ export ANDROID_SDK_ROOT=$HOME/Android/Sdk
   ```
2. Add the Android SDK command-line directories to `PATH`. Each directory corresponds to the category of [command-line tool](https://developer.android.com/studio/command-line/).

   ```
   $ # avdmanager, sdkmanager$ 
   export PATH=$PATH:$ANDROID_SDK_ROOT/tools/bin$ 
   # adb, logcat$ 
   export PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools$ 
   # emulator$ 
   export PATH=$PATH:$ANDROID_SDK_ROOT/emulator
   ```

### Java[​](https://ionicframework.com/docs/developing/android#java)

Native Android apps are compiled with the [Java](https://java.com/en/) programming language. Download JDK8 from the [download page](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html).

### Gradle

[Gradle](https://gradle.org/) is the build tool used in Android apps and must be installed separately. See the [install page](https://gradle.org/install/) for details.

## Project Setup <a href="#project-setup" id="project-setup"></a>

If you already have all the above tools installed, you should be able to get up and running within a few minutes. Let's start from scratch by creating a blank project.

<figure><img src="/files/80ULtHFtUKjALlPH4MQc" alt=""><figcaption><p>Create a Project with Blank Activity</p></figcaption></figure>

<figure><img src="/files/L596rY5yEYXm3PplezGP" alt=""><figcaption><p>Name, Location &#x26; Configurations</p></figcaption></figure>

Wait for the Gradle sync to complete. Now we are good to jump into the Connector SDK setup.

## Connector SDK Setup

The Connector SDK offers the same APIs on all supported platforms, providing a fast learning curve and enabling rapid development on all device types.

### Download & Locate

First of all, we have to Download the latest Connector SDK Package for Android. You can download it under [Resources](/vidyoplatform/resources.md) section of VidyoPlatfrom Space or use the following link:

[Connector SDK for Android](https://static.vidyo.io/22.5.0.8/package/VidyoClient-AndroidSDK.zip)

* Download and unzip VidyoClient-AndroidSDK package content.&#x20;
* Locate `VidyoClient.aar` under `/VidyoClient-AndroidSDK/lib/android/`
* Copy `VidyoClient.aar` to your project under `/app/libs/`

<figure><img src="/files/yduuTYWPNV57aFVaZvQr" alt=""><figcaption><p>VidyoClient.aar location</p></figcaption></figure>

### Link Connector SDK&#x20;

In order to link the Connector SDK `AAR` Library we have to tell our Gradle sync to to look into `/app/libs` a folder and lookup for `*.aar` files specifically. Open `app/build.gradle` configurations file and apply `fileTree` dependency implementation:

```
implementation fileTree(dir: 'libs', include: ['*.aar'])
```

<figure><img src="/files/NsXPYOLckAaR0D5iRTk1" alt=""><figcaption><p>Link VidyoClient.aar</p></figcaption></figure>

{% hint style="info" %}
Hit `Sync Now` popup hint to apply the changes.
{% endhint %}

&#x20;Now we are good to start over with Basic Connector SDK implementation.

## Connector SDK Implementation

Connector SDK has a very simple API that only requires three basic steps:

1. When using the client library, the first step is to initialize the Connector SDK
2. Construct and pass the [View](https://developer.android.com/reference/android/view/View) where the preview and participants should be rendered.
3. Connect to the live conference. A developer can specify the portal and room where they want all the live participants to connect.

### Setup Android Permissions

Connector SDK requires Android-specific [runtime permissions](https://developer.android.com/training/permissions/requesting), which we have to request before initializing SDK along with the Basic permissions for Networking.&#x20;

```java
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<!-- RUNTIME PERMISSIONS -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
```

Add those permissions to your `AndroidManifest.xml`

Now let's add the following code in order to request Runtime Permissions under our `MainActivity`:

```java
private static final int PERMISSIONS_REQUEST_CODE = 0x144;
private static final String[] PERMISSIONS = new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};

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

/**
 * Verify or Request Runtime Permissions every time before attempting to initialize Connector SDK
 */
private void requestPermissions() {
        List<String> permissionsRequired = new ArrayList<>();
        for (String permission : PERMISSIONS) {
            if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED)
                permissionsRequired.add(permission);
        }

        if (!permissionsRequired.isEmpty()) {
            ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSIONS_REQUEST_CODE);
        } else {
            onPermissionsAllowed();
        }
} 

/**
 * Here we are safe to initialize a Connector SDK
 */
private void onPermissionsAllowed() {

}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == PERMISSIONS_REQUEST_CODE) {
        onPermissionsAllowed();
    }
}
```

<figure><img src="/files/BJkAENkczUewlYWa8sQ7" alt=""><figcaption><p>Request Runtime Permissions</p></figcaption></figure>

### Initialize Connector SDK

Once we have passed through sequential permissions requests (the app will ask for Camera & Audio permissions) we are good to initialize Connector SDK.

```java
ConnectorPkg.initialize();
ConnectorPkg.setApplicationUIContext(this);
```

{% hint style="info" %}
Obviously, ConnectorPkg will be highlighted as an unknown class. Use auto-import or correct the import package manually:

```java
import com.vidyo.VidyoClient.Connector.ConnectorPkg;
```

{% endhint %}

<figure><img src="/files/pk0GyQNS0mQMcQ2mlJv5" alt=""><figcaption></figcaption></figure>

### Construct Connector API

Connector Object is the main communication instance with the Connector SDK by leveraging all the API calls and managing the SDK Library lifecycle. Base concept APIs that we are going to review in the scope of this Guide:

* Construct Connector Object: `new Connector`&#x20;
* Update video renderer: `showViewAt`&#x20;
* Connect to the call: `connectToRoomAsGuest`&#x20;
* Disconnect from the call: `disconnect`
* Get Connector state: `getState`
* Manage Connector lifecycle: `setMode`
* Destroy connector instance: `disable`

We are going to create a new connector instance right after SDK initialization. But first of all, let's have a look at the constructor parameters:

```java
Connector(Object viewId, 
    Connector.ConnectorViewStyle viewStyle, 
    int remoteParticipants, 
    String logFileFilter, 
    String logFileName, 
    long userData);
```

| Name               | Description                                                                                                                                    |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| viewId             | A platform-specific view ID where the VidyoConnector's rendering window will be added as a child window.                                       |
| viewStyle          | Type of the composite renderer which represents the visual style and behaviour.                                                                |
| remoteParticipants | Number of remote participants to composite into the window. Setting the value to 0 (zero) will render the preview only.                        |
| logFileFilter      | A space-separated (or comma-separated) sequence of names of log levels, each optionally followed by a category.                                |
| logFileName        | Full path to the file where the log should be stored; otherwise, NULL or empty string, in order to use the default OS-dependent writable path. |
| userData           | Arbitrary user data that can be retrieved later.                                                                                               |

Guess we can populate everything except `viewId`. For this, we have to create a [ViewGroup](https://developer.android.com/reference/android/view/ViewGroup) container and pass it as a `viewId` reference. Visit your `activity_main.xml` UI layout file and add any kind of ViewGroup expanding full screen.

```xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/video_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

Now let's create a Connector Object:

<pre class="language-java"><code class="lang-java">public class MainActivity extends AppCompatActivity implements ViewTreeObserver.OnGlobalLayoutListener {
    
    // ...

    private Connector mConnector;
    private FrameLayout mVideoContainer;
    
    // ...

    /**
     * Here we are safe to initialize a Connector SDK
     */
    private void onPermissionsAllowed() {
         ConnectorPkg.initialize();
         ConnectorPkg.setApplicationUIContext(this);

         mVideoContainer = findViewById(R.id.video_container);
        
         mConnector = new Connector(videoContainer, 
                Connector.ConnectorViewStyle.VIDYO_CONNECTORVIEWSTYLE_Default, 
                8, 
                "warning debug@VidyoClient",
                "", 
                0);

        mVideoContainer.getViewTreeObserver().addOnGlobalLayoutListener(this)
    }

    @Override
<strong>    public void onGlobalLayout() {
</strong>       int width = mVideoContainer.getWidth();
       int height = mVideoContainer.getHeight();

       mConnector.showViewAt(mVideoContainer, 0, 0, width, height);
<strong>    }
</strong>}
</code></pre>

In order to render self-view or any remote participant, you have to tell the library your View's dimension and renderer position:

```java
mConnector.showViewAt(mVideoContainer, 0, 0, width, height);
```

Moreover, you have to do it every time the View's layout has changed or manipulated. In this guide, we'll use a robust `OnGlobalLayoutListener`, however, you are good to go with your own solution.\
Don't forget to unregister from `OnGlobalLayoutListener` updates:

```java
@Override
protected void onDestroy() {
    super.onDestroy();
    mVideoContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
```

### Host and RoomKey

In the previous tutorial you created a room on your tenant and received a RoomLink:

{% embed url="<https://tenant_name.platform.vidyo.io/join/bPFCSWVZnb>" %}
Room URL
{% endembed %}

As you may guess, the following chunk of the above link is your Host value:

{% embed url="<https://tenant_name.platform.vidyo.io/>" %}
Host (FQDN - Fully Qualified Domain Name)
{% endembed %}

And this one is a RoomKey:

```
bPFCSWVZnb
```

{% hint style="info" %}
Host (or your tenant URL) and RoomKey uniquely identify the meeting room. In other words - these are two values that your client application should get in order to connect to the room.

In a real-world typically these values are sent to the client apps from the backend server application after the room has been created.
{% endhint %}

After we got the necessary parameters (*host*, *roomKey*, *displayName*) we can proceed with joining the call.

### Connect to the Call

Before we start executing Connector API let's add some basic interaction UI. We are going to have Connect/Disconnect [Button](https://developer.android.com/reference/android/widget/Button). That's all we need to hop on the call or disconnect. Additionally, we would like to indicate that the call connection is in progress with basic [ProgressBar](https://developer.android.com/reference/android/widget/ProgressBar). Modify your `activity_main.xml` UI layout file:&#x20;

```xml
<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:id="@+id/video_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:id="@+id/connect_disconnect_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="24dp"
        android:text="Connect"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <ProgressBar
        android:id="@+id/connection_progress_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.5" />
        
</androidx.constraintlayout.widget.ConstraintLayout>
```

In order to connect to the call, you should have your `Host` and `RoomKey` already prepared as stated in the section above or [Getting Started](/vidyoplatform/getting-started.md) section.&#x20;

* Execute `connectToRoomAsGuest` Connector API and connect to the call
* Execute `disconnect` Connector API in order to disconnect from the call
* To differentiate Connector's Connected|Disconnected state, we'll use `getState` API

<pre class="language-java"><code class="lang-java">// ...
private Button mConnectBtn;
private ProgressBar mConnectionProgress;

// ..

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

    mVideoContainer = findViewById(R.id.video_container);

    mConnectBtn = findViewById(R.id.connect_disconnect_btn);
    mConnectBtn.setOnClickListener(this);

    mConnectionProgress = findViewById(R.id.connection_progress_bar);
    mConnectionProgress.setVisibility(View.GONE);
    
    requestPermissions();
}

// ...

 @Override
 public void onClick(View view) {
    if (view == mConnectBtn) {
        boolean isConnected = mConnector.getState() == Connector.ConnectorState.VIDYO_CONNECTORSTATE_Connected;
<strong>
</strong>        mConnectionProgress.setVisibility(View.VISIBLE); 
        mConnectBtn.setEnabled(false);
           
        if (!isConnected) {
            mConnectBtn.setText("Connecting...");
<strong>            
</strong>            mConnector.connectToRoomAsGuest("HOST", "DISPLAY NAME", "ROOM KEY", "ROOM PIN",
                    new Connector.IConnect() {

                        @Override
                        public void onSuccess() {
                            runOnUiThread(() -> {
                                mConnectBtn.setEnabled(true);
                                mConnectBtn.setText("Disconnect");
                                mConnectionProgress.setVisibility(View.GONE);
                            });
                        }

                        @Override
                        public void onFailure(Connector.ConnectorFailReason connectorFailReason) {
                            runOnUiThread(() -> {
                                mConnectBtn.setEnabled(true);
                                mConnectionProgress.setVisibility(View.GONE);
                                Toast.makeText(MainActivity.this, "Connection failed: " + connectorFailReason, Toast.LENGTH_LONG).show();
                            });
                        }

                        @Override
                        public void onDisconnected(Connector.ConnectorDisconnectReason connectorDisconnectReason) {
                            runOnUiThread(() -> {
                                mConnectBtn.setEnabled(true);
                                mConnectionProgress.setVisibility(View.GONE);
                                mConnectBtn.setText("Connect");
                            });
                        }
                    });
            } else {
                mConnectBtn.setText("Disconnecting...");
                mConnector.disconnect();
            }
    }
}
</code></pre>

#### Callbacks

There are three callback functions in the API call above:

* *onSuccess()*: will be fired when connection to the room went successfully. This place is an indicator of the fact that your application has joined the call.
* *onFailure()*: will be fired when connection attempt was made, but something went wrong and your app hasn't joined the call. *Reason* parameter may provide you with details why connection attempt was not successful.
* *onDisconnected()*: will be fired when your application disconnected from the call.   &#x20;

As you might notice, all the callbacks coming from Connector SDK are routed to the `Main UI Thread` with `runOnUiThread` Activity's API. It's because the library is executing them from its own Background Thread where you cannot touch or update UI.

{% hint style="info" %}
Don't forget to populate your Conference Credentials:

"HOST" | "DISPLAY NAME" | "ROOM KEY" | "ROOM PIN"

Leave "Room Pin" as an empty "" if not specified.
{% endhint %}

### Disconnect from the call

Connector `disconnect` API has been mentioned earlier, however, the important notice is to properly manage the disconnection state. It's not recommended to wrap the activity during the active conference, therefore, you have to disconnect first and wrap up the connector instance gracefully.

Assume, the user would like to quit the Activity. We have to prevent this action until further disconnection:

```java
private boolean mUserQuitRequest = false;

// ....

@Override
public void onBackPressed() {
    if (mConnector.getState() == Connector.ConnectorState.VIDYO_CONNECTORSTATE_Connected) {
        mUserQuitRequest = true;
        mConnector.disconnect();
    } else {
        super.onBackPressed();
    }
}

// ...

@Override
 public void onClick(View view) {
    if (view == mConnectBtn) {
        boolean isConnected = mConnector.getState() == Connector.ConnectorState.VIDYO_CONNECTORSTATE_Connected;

        mConnectionProgress.setVisibility(View.VISIBLE); 
        mConnectBtn.setEnabled(false);
           
        if (!isConnected) {
            mConnectBtn.setText("Connecting...");
            
            mConnector.connectToRoomAsGuest("HOST", "DISPLAY NAME", "ROOM KEY", "ROOM PIN",
                    new Connector.IConnect() {

                        @Override
                        public void onSuccess() {
                            runOnUiThread(() -> {
                                mConnectBtn.setEnabled(true);
                                mConnectBtn.setText("Disconnect");
                                mConnectionProgress.setVisibility(View.GONE);
                            });
                        }

                        @Override
                        public void onFailure(Connector.ConnectorFailReason connectorFailReason) {
                            runOnUiThread(() -> {
                                mConnectBtn.setEnabled(true);
                                mConnectionProgress.setVisibility(View.GONE);
                                Toast.makeText(MainActivity.this, "Connection failed: " + connectorFailReason, Toast.LENGTH_LONG).show();
                                
                                if (mUserQuitRequest) {
                                    MainActivity.super.onBackPressed();
                                }
                            });
                        }

                        @Override
                        public void onDisconnected(Connector.ConnectorDisconnectReason connectorDisconnectReason) {
                            runOnUiThread(() -> {
                                mConnectBtn.setEnabled(true);
                                mConnectionProgress.setVisibility(View.GONE);
                                mConnectBtn.setText("Connect");

                                if (mUserQuitRequest) {
                                    MainActivity.super.onBackPressed();
                                }
                            });
                        }
                    });
            } else {
                mConnectBtn.setText("Disconnecting...");
                mConnector.disconnect();
            }
    }
}
```

`mUserQuitRequest` will hold the quit request state until `onDisconnected` or `onFailure` in order to re-test `onBackPressed` action.

### Manage Lifecycle

You have to tell Connector Object about lifecycle state change so he can manage his internal flow accordingly. `setMode` is the proper API to do this.

```java
@Override
protected void onResume() {
    super.onResume();
    if (mConnector != null)
        mConnector.setMode(Connector.ConnectorMode.VIDYO_CONNECTORMODE_Foreground);
}

@Override
protected void onPause() {
    super.onPause();
    if (mConnector != null)
        mConnector.setMode(Connector.ConnectorMode.VIDYO_CONNECTORMODE_Background);
}
```

### Destruct Connector Instance

The last and most important part is the "Connector Object deallocation". Ideally, you have to do this along with the Activity lifecycle to prevent a further memory leak. To sync them together, we'll use `onDestroy` Activity's lifecycle callback. At this point, we are insured that `mConnector` state is not "Connected" and we are safe to call `disable` API.

```java
 @Override
 protected void onDestroy() {
    super.onDestroy();
    mVideoContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    // Destroy connector instance    
    if (mConnector != null)
        mConnector.disable();
 }
```

{% hint style="info" %}
Since we disconnect before "Quit" with our custom logic, we can guarantee that during onDestroy() callback the mConnector state is "Disconnected". In case your application flow is different, the only rule you have to remember is to call "disable" API at a "disconnected" state. \
\
We might change this behavior in the future so you will not bother about the connection state. Until now, it's a mandatory action to prevent crashes and memory leaks.
{% endhint %}


---

# 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://enghouse-vidyo.gitbook.io/vidyoplatform/android-integration.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.
