How to Make a 1-on-1 Video Chat Android Java App with Video SDK
Tags: android, java, developer
Published: 2023-07-06 04:30:00

### Introduction
Remote communication has become the most important part of communication after the pandemic. It's highly likely that it will play a significant role in future too. Today's mobile applications often include voice or video calling functionality. But it's extremely complex and takes a lot of time to build. This is where [Video SDK](https://www.videosdk.live/) comes into the picture.

Video SDK is a platform that allows developers to create rich in-app experiences such as embed real-time video, voice, real-time recording, live streaming and real-time messaging.

Video SDK is available for [JavaScript](https://en.wikipedia.org/wiki/JavaScript), [ReactJS](https://en.wikipedia.org/wiki/React_(software)), [React-Native](https://en.wikipedia.org/wiki/React_Native), [IOS](https://en.wikipedia.org/wiki/IOS), [Android](https://en.wikipedia.org/wiki/Android_(operating_system)) and [Flutter](https://en.wikipedia.org/wiki/Flutter_(software)) to be integrated seamlessly. Video SDK also provides a Pre-built SDK that enables opportunity to integrate real time communication with your application with just 10 minutes!

Let's create one-to-one video calling app using Video SDK.
But first, we need to create Video SDK account and generate token.

## Prerequisites
First of all, Your development environment should meet the following requirements:

- [Java Development Kit](https://www.oracle.com/in/java/technologies/downloads/).
- [Android Studio](https://developer.android.com/studio) 3.0 or later.
- Android SDK API Level 21 or higher.
- A mobile device that runs Android 5.0 or later.

## Setup the project
### Step 1: Create new Project
Let’s start by creating new project. In Android Studio, create a new project with an Empty Activity.
Then, provide a name. We will name it as OneToOneDemo.
### Step 2: Integrate Video SDK
- Add the repository to `settings.gradle` file.

```js
dependencyResolutionManagement{
  repositories {
    // ...
    google()
    mavenCentral()
    maven { url 'https://jitpack.io' }
    maven { url "https://maven.aliyun.com/repository/jcenter" }
  }
}
```
- Add the following dependencies in `build.gradle` file.

```js
dependencies {
  implementation 'live.videosdk:rtc-android-sdk:0.1.13'

  // library to perform Network call to generate a meeting id
  implementation 'com.amitshekhar.android:android-networking:1.0.2'

  // other app dependencies
  }
```

> If your project has set `android.useAndroidX = true`, then set `android.enableJetifier = true` in the `gradle.properties` file to migrate your project to AndroidX and avoid duplicate class conflict.

### Step 3: Add permissions into your project

In `/app/Manifests/AndroidManifest.xml`, add the following permissions after `</application>`.

```js
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
```
## Getting Started with the Code!
Structure of project
We'll create two screens. First screen will be `Joining screen` that allows user to create/join the meeting, and another one is `Meeting screen` that will show participants like Whatsapp view.

Our project structure would look like this.

```js
   app
   ├── java
   │    ├── packagename
   │         ├── JoinActivity
   │         ├── MeetingActivity
   ├── res
   │    ├── layout
   │    │    ├── activity_join.xml
   │    │    ├── activity_meeting.xml
```

> You have to set `JoinActivity` as Launcher activity.

### Creating Joining Screen
Create a new Activity named `JoinActivity`

### Creating UI for Joining Screen
The Joining screen will include :

1. Create Button - Creates a new meeting.
2. TextField for Meeting Id - Contains the meeting Id you want to join.
3. Join Button - Joins the meeting with the meetingId provided.

In `/app/res/layout/activity_join.xml` file, replace the content with the following.

```js
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".JoinActivity">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/material_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:contentInsetStart="0dp"
        android:background="?attr/colorPrimary"
        app:titleTextColor="@color/white" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical">


        <Button
            android:id="@+id/btnCreateMeeting"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:text="Create Meeting" />

        <TextView
            style="@style/TextAppearance.AppCompat.Headline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="OR" />

        <com.google.android.material.textfield.TextInputLayout        	style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginVertical="16dp"
            android:hint="Enter Meeting ID">

            <EditText
                android:id="@+id/etMeetingId"
                android:layout_width="250dp"
                android:layout_height="wrap_content" />
        </com.google.android.material.textfield.TextInputLayout>

        <Button
            android:id="@+id/btnJoinMeeting"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Join Meeting" />
    </LinearLayout>

</LinearLayout>
```
### Integration of Create Meeting API
- You need to create a field sampleToken in JoinActivity that holds the generated token from the Video SDK dashboard. This token will be used in Video SDK config as well as generating meetingId.

```js
public class JoinActivity extends AppCompatActivity {

  //Replace with the token you generated from the VideoSDK Dashboard
  private String sampleToken = ""; 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    //...
  }
}
```
- On Join Button `onClick` event, it will naviagte to `MeetingActivity` with token and meetingId.

```js
public class JoinActivity extends AppCompatActivity {

  //Replace with the token you generated from the VideoSDK Dashboard
  private String sampleToken =""; 

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

    final Button btnCreate = findViewById(R.id.btnCreateMeeting);
    final Button btnJoin = findViewById(R.id.btnJoinMeeting);
    final EditText etMeetingId = findViewById(R.id.etMeetingId);

    //set title
    Toolbar toolbar = findViewById(R.id.material_toolbar);
    toolbar.setTitle("OneToOneDemo");
    setSupportActionBar(toolbar);
        
    btnCreate.setOnClickListener(v -> {
      // we will explore this method in the next step
      createMeeting(sampleToken);
    });

    btnJoin.setOnClickListener(v -> {
      Intent intent = new Intent(JoinActivity.this, MeetingActivity.class);
      intent.putExtra("token", sampleToken);
      intent.putExtra("meetingId", etMeetingId.getText().toString());
      startActivity(intent);
    });
  }

  private void createMeeting(String token) {
  }
}
```
- For **Create Button**, under `createMeeting` method we will generate meetingId by calling API and navigate to `MeetingActivity` with token and generated meetingId.

```js
public class JoinActivity extends AppCompatActivity {
  //...onCreate

  private void createMeeting(String token) {
      // we will make an API call to VideoSDK Server to get a roomId
      AndroidNetworking.post("https://api.videosdk.live/v2/rooms")
        .addHeaders("Authorization", token) //we will pass the token in the Headers
        .build()
        .getAsJSONObject(new JSONObjectRequestListener() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    // response will contain `roomId`
                    final String meetingId = response.getString("roomId");

                    // starting the MeetingActivity with received roomId and our sampleToken
                    Intent intent = new Intent(JoinActivity.this, MeetingActivity.class);
                    intent.putExtra("token", sampleToken);
                    intent.putExtra("meetingId", meetingId);
                    startActivity(intent);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onError(ANError anError) {
                anError.printStackTrace();
                Toast.makeText(JoinActivity.this, anError.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
  }
}
```
- Our App is completely based on audio and video commutation, that's why we need to ask for runtime permissions `RECORD_AUDIO` and `CAMERA`. So, we will implement permission logic on `JoinActivity`.

```js
public class JoinActivity extends AppCompatActivity {
  private static final int PERMISSION_REQ_ID = 22;

  private static final String[] REQUESTED_PERMISSIONS = {
    Manifest.permission.RECORD_AUDIO,
    Manifest.permission.CAMERA
  };

  private boolean checkSelfPermission(String permission, int requestCode) {
    if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
      ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
      return false;
    }
    return true;
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    //... button listeneres
    checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID);
    checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID);
  }
}
```

> You'll get `Unresolved reference: MeetingActivity` error, but don't worry. It will be solved automatically once you create `MeetingActivity`.

- We're done the Joining screen now, and it is time to create the participant's view in the Meeting screen.

### Step 4: Creating Meeting Screen
Create a new Activity named `MeetingActivity`.

### Creating the UI for Meeting Screen
In `/app/res/layout/activity_meeting.xml` file, replace the content with the following.

```js
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MeetingActivity">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/material_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/black"
        app:contentInsetStart="0dp"
        app:titleTextColor="@color/white">

        <LinearLayout
            android:id="@+id/meetingLayout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="14dp"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:id="@+id/txtMeetingId"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:fontFamily="sans-serif-medium"
                    android:textColor="@color/white"
                    android:textFontWeight="600"
                    android:textSize="16sp" />

                <ImageButton
                    android:id="@+id/btnCopyContent"
                    android:layout_width="22dp"
                    android:layout_height="22sp"
                    android:layout_marginLeft="7dp"
                    android:layout_toRightOf="@+id/txtMeetingId"
                    android:backgroundTint="@color/black"
                    android:src="@drawable/ic_outline_content_copy_24" />

            </RelativeLayout>

        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:layout_marginEnd="10dp">

            <ImageButton
                android:id="@+id/btnSwitchCameraMode"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@color/black"
                android:contentDescription="Switch Camera mode"
                android:src="@drawable/ic_baseline_flip_camera_android_24" />

        </LinearLayout>

    </com.google.android.material.appbar.MaterialToolbar>

    <FrameLayout
        android:id="@+id/participants_frameLayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/black">

        <androidx.cardview.widget.CardView
            android:id="@+id/ParticipantCard"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="12dp"
            android:layout_marginTop="3dp"
            android:layout_marginRight="12dp"
            android:layout_marginBottom="3dp"
            android:backgroundTint="#2B3034"
            android:visibility="gone"
            app:cardCornerRadius="8dp"
            app:strokeColor="#2B3034">

            <ImageView
                android:layout_width="150dp"
                android:layout_height="150dp"
                android:layout_gravity="center"
                android:src="@drawable/ic_baseline_person_24" />

            <live.videosdk.rtc.android.VideoView
                android:id="@+id/participantView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="gone" />

        </androidx.cardview.widget.CardView>

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </FrameLayout>

        <androidx.cardview.widget.CardView
            android:id="@+id/LocalCard"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="12dp"
            android:layout_marginTop="3dp"
            android:layout_marginRight="12dp"
            android:layout_marginBottom="3dp"
            android:backgroundTint="#1A1C22"
            app:cardCornerRadius="8dp"
            app:strokeColor="#1A1C22">

            <ImageView
                android:id="@+id/localParticipant_img"
                android:layout_width="150dp"
                android:layout_height="150dp"
                android:layout_gravity="center"
                android:src="@drawable/ic_baseline_person_24" />

            <live.videosdk.rtc.android.VideoView
                android:id="@+id/localView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="gone" />

        </androidx.cardview.widget.CardView>

    </FrameLayout>
    
    <!-- add bottombar here-->

</LinearLayout>
```

```js
<com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bottomAppbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:backgroundTint="@color/black"
        android:gravity="center_horizontal"
        android:paddingVertical="5dp"
        tools:ignore="BottomAppBar">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingStart="16dp"
            android:paddingEnd="16dp">

            <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/btnLeave"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:contentDescription="Leave Meeting"
                android:src="@drawable/ic_end_call"
                app:backgroundTint="#FF5D5D"
                app:fabSize="normal"
                app:tint="@color/white" />

            <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/btnMic"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="90dp"
                android:layout_toEndOf="@+id/btnLeave"
                android:contentDescription="Toggle Mic"
                android:src="@drawable/ic_mic_off"
                app:backgroundTint="@color/white"
                app:borderWidth="1dp"
                app:fabSize="normal" />

            <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/btnWebcam"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="90dp"
                android:layout_toEndOf="@+id/btnMic"
                android:backgroundTint="@color/white"
                android:contentDescription="Toggle Camera"
                android:src="@drawable/ic_video_camera_off"
                app:backgroundTint="@color/white"
                app:borderWidth="1dp"
                app:fabSize="normal" />

        </RelativeLayout>

    </com.google.android.material.bottomappbar.BottomAppBar>
```

> Copy required icons from here and paste in your project's `res/drawable` folder.

#### Initializing the Meeting
After getting token and meetingId from `JoinActivity`, we need to...

- Initialize **Video SDK**
- Configure **Video SDK** with token.
- Initialize the meeting with required params such as `meetingId`, `participantName`, `micEnabled`, `webcamEnabled,participantId` and map of `CustomStreamTrack`.
- Join the room with `meeting.join()` method.

```js
public class MeetingActivity extends AppCompatActivity {

    private static Meeting meeting;
    private boolean micEnabled = true;
    private boolean webcamEnabled = true;

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

        //
        Toolbar toolbar = findViewById(R.id.material_toolbar);
        toolbar.setTitle("");
        setSupportActionBar(toolbar);

        //
        String token = getIntent().getStringExtra("token");
        final String meetingId = getIntent().getStringExtra("meetingId");

        // set participant name
        String localParticipantName = "Alex";

        // Initialize VideoSDK
        VideoSDK.initialize(getApplicationContext());

        // pass the token generated from api server
        VideoSDK.config(token);

        // create a new meeting instance
        meeting = VideoSDK.initMeeting(
                MeetingActivity.this, meetingId, localParticipantName,
                micEnabled, webcamEnabled, null, null
        );

        // join the meeting
        if (meeting != null) meeting.join();

        //
        TextView textMeetingId = findViewById(R.id.txtMeetingId);
        textMeetingId.setText(meetingId);

	 // copy meetingId to clipboard
         ((ImageButton) findViewById(R.id.btnCopyContent)).setOnClickListener(v -> copyTextToClipboard(meetingId));

   }
      
      private void copyTextToClipboard(String text) {
        ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("Copied text", text);
        clipboard.setPrimaryClip(clip);

        Toast.makeText(MeetingActivity.this, "Copied to clipboard!", Toast.LENGTH_SHORT).show();
    }

}
```
### Step 5: Handle Local Participant Media
We need to implement clicks for the following `Views`:

- Mic Button
- Webcam Button
- Switch Camera Button
- Leave Button

Add the following implementation:

```js
public class MeetingActivity extends AppCompatActivity {

	private FloatingActionButton btnWebcam, btnMic, btnLeave;
    private ImageButton btnSwitchCameraMode;
    
  @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);
    
    //
    btnMic = findViewById(R.id.btnMic);
    btnWebcam = findViewById(R.id.btnWebcam);
    btnLeave = findViewById(R.id.btnLeave);
    btnSwitchCameraMode = findViewById(R.id.btnSwitchCameraMode);
        
    //...

    // actions
    setActionListeners();
  }
  
  private void setActionListeners() {
        // Toggle mic
        btnMic.setOnClickListener(view -> toggleMic());

        // Toggle webcam
        btnWebcam.setOnClickListener(view -> toggleWebCam());
        
        // Leave meeting
        btnLeave.setOnClickListener(view -> {
            // this will make the local participant leave the meeting
            meeting.leave();
        });

        // Switch camera
        btnSwitchCameraMode.setOnClickListener(view -> {
            //a participant can change stream from front/rear camera during the meeting.
            meeting.changeWebcam();
        });
    
   }
}
```

```js
private void toggleMic() {
        if (micEnabled) {
            // this will mute the local participant's mic
            meeting.muteMic();
        } else {
            // this will unmute the local participant's mic
            meeting.unmuteMic();
        }
        micEnabled = !micEnabled;
        // change mic icon according to micEnable status
        toggleMicIcon();
    }

    @SuppressLint("ResourceType")
    private void toggleMicIcon() {
        if (micEnabled) {
            btnMic.setImageResource(R.drawable.ic_mic_on);
            btnMic.setColorFilter(Color.WHITE);
            Drawable buttonDrawable = btnMic.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.TRANSPARENT);
            btnMic.setBackground(buttonDrawable);

        } else {
            btnMic.setImageResource(R.drawable.ic_mic_off);
            btnMic.setColorFilter(Color.BLACK);
            Drawable buttonDrawable = btnMic.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.WHITE);
            btnMic.setBackground(buttonDrawable);
        }
    }
```

```js
private void toggleWebCam() {
        if (webcamEnabled) {
            // this will disable the local participant webcam
            meeting.disableWebcam();
        } else {
            // this will enable the local participant webcam
            meeting.enableWebcam();
        }
        webcamEnabled = !webcamEnabled;
        // change webCam icon according to webcamEnabled status
        toggleWebcamIcon();
    }

    @SuppressLint("ResourceType")
    private void toggleWebcamIcon() {
        if (webcamEnabled) {
            btnWebcam.setImageResource(R.drawable.ic_video_camera);
            btnWebcam.setColorFilter(Color.WHITE);
            Drawable buttonDrawable = btnWebcam.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.TRANSPARENT);
            btnWebcam.setBackground(buttonDrawable);

        } else {
            btnWebcam.setImageResource(R.drawable.ic_video_camera_off);
            btnWebcam.setColorFilter(Color.BLACK);
            Drawable buttonDrawable = btnWebcam.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.WHITE);
            btnWebcam.setBackground(buttonDrawable);
        }
    }
```
#### Setting up Local participant view
To set up participant view, we have to implement all the methods of the `ParticipantEventListener` abstract Class and add the listener to `Participant` class using the `addEventListener()` method of `Participant` Class. `ParticipantEventListener` class have two methods :

- `onStreamEnabled` - Whenever a participant enables mic/webcam in meeting, This event will be triggered and return `Stream`.
- `onStreamDisabled` - Whenever a participant disables mic/webcam in meeting, This event will be triggered and return `Stream`.

```js
public class MeetingActivity extends AppCompatActivity {

    private VideoView localView;
    private VideoView participantView;
    private CardView localCard, participantCard;
    private ImageView localParticipantImg;
    
  @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);
    
    //... 
    
    localCard = findViewById(R.id.LocalCard);
    participantCard = findViewById(R.id.ParticipantCard);
    localView = findViewById(R.id.localView);
    participantView = findViewById(R.id.localParticipant);
    localParticipantImg = findViewById(R.id.localParticipant_img);
        
    //...

    // setup local participant view
    setLocalListeners();
  }

  private void setLocalListeners() {
        meeting.getLocalParticipant().addEventListener(new ParticipantEventListener() {
            @Override
            public void onStreamEnabled(Stream stream) {
                if (stream.getKind().equalsIgnoreCase("video")) {
                    VideoTrack track = (VideoTrack) stream.getTrack();
                    localView.setVisibility(View.VISIBLE);
                    localView.addTrack(track);
                    localView.setZOrderMediaOverlay(true);
                    localCard.bringToFront();
                }
            }

            @Override
            public void onStreamDisabled(Stream stream) {
                if (stream.getKind().equalsIgnoreCase("video")) {
                    localView.removeTrack();
                    localView.setVisibility(View.GONE);
                }
            }
        });
    }
}
```
#### Setting up Remote participant view

```js
private final ParticipantEventListener participantEventListener = new ParticipantEventListener() {
        // trigger when participant enabled mic/webcam
        @Override
        public void onStreamEnabled(Stream stream) {
            if (stream.getKind().equalsIgnoreCase("video")) {
                localView.setZOrderMediaOverlay(true);
                localCard.bringToFront();
                VideoTrack track = (VideoTrack) stream.getTrack();
                participantView.setVisibility(View.VISIBLE);
                participantView.addTrack(track);
            }
        }

        // trigger when participant disabled mic/webcam
        @Override
        public void onStreamDisabled(Stream stream) {
            if (stream.getKind().equalsIgnoreCase("video")) {
                participantView.removeTrack();
                participantView.setVisibility(View.GONE);
            }
        }
    };
```
#### Handle meeting events & manage participant's view
- Add `MeetingEventListener` for listening events such as Meeting Join/Left and Participant Join/Left.

```js
public class MeetingActivity extends AppCompatActivity {
  @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);
    //...

    // handle meeting events
    meeting.addEventListener(meetingEventListener);
  }
  
  private final MeetingEventListener meetingEventListener = new MeetingEventListener() {
        @Override
        public void onMeetingJoined() {
            // change mic,webCam icon after meeting successfully joined
            toggleMicIcon();
            toggleWebcamIcon();
        }

        @Override
        public void onMeetingLeft() {
            if (!isDestroyed()) {
                Intent intent = new Intent(MeetingActivity.this, JoinActivity.class);
                startActivity(intent);
                finish();
            }
        }

        @Override
        public void onParticipantJoined(Participant participant) {
            // Display local participant as miniView when other participant joined
            changeLocalParticipantView(true);
            Toast.makeText(MeetingActivity.this, participant.getDisplayName() + " joined",
                    Toast.LENGTH_SHORT).show();
            participant.addEventListener(participantEventListener);
        }

        @Override
        public void onParticipantLeft(Participant participant) {
            // Display local participant as largeView when other participant left
            changeLocalParticipantView(false);
            Toast.makeText(MeetingActivity.this, participant.getDisplayName() + " left",
                    Toast.LENGTH_SHORT).show();
        }
    };
}
```

- `changeLocalParticipantView(isMiniView: Boolean)` function first checks whether the video of a local participant is displayed as a MiniView or a LargeView.
- If the meeting has only one participant (local participant), then local participant is displayed as LargeView.
- When another participant (other than the local participant) joins, `changeLocalParticipantView(true)` is called. And as a result, the local participant is shown as MiniView, while the other participant is shown as LargeView.

```js
private void changeLocalParticipantView(boolean isMiniView) {
        if(isMiniView)
        {
            // show localCard as miniView
            localCard.setLayoutParams(new CardView.LayoutParams(300, 430, Gravity.RIGHT | Gravity.BOTTOM));
            ViewGroup.MarginLayoutParams cardViewMarginParams = (ViewGroup.MarginLayoutParams) localCard.getLayoutParams();
            cardViewMarginParams.setMargins(30, 0, 60, 40);
            localCard.requestLayout();
            // set height-width of localParticipant_img
            localParticipantImg.setLayoutParams(new FrameLayout.LayoutParams(150, 150, Gravity.CENTER));
            participantCard.setVisibility(View.VISIBLE);
        }else{
            // show localCard as largeView
            localCard.setLayoutParams(new CardView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
            ViewGroup.MarginLayoutParams cardViewMarginParams = (ViewGroup.MarginLayoutParams) localCard.getLayoutParams();
            cardViewMarginParams.setMargins(30, 5, 30, 30);
            localCard.requestLayout();
            // set height-width of localParticipant_img
            localParticipantImg.setLayoutParams(new FrameLayout.LayoutParams(400, 400, Gravity.CENTER));
            participantCard.setVisibility(View.GONE);
        }
    }
```
#### Destroying everything
We need to release resources when the app is closed and is no longer being used. Override the `onDestroy` with following code:

```js
protected void onDestroy() {
        if(meeting !=null)
        {
            meeting.removeAllListeners();
            meeting.getLocalParticipant().removeAllListeners();
            meeting.leave();
            meeting = null;
        }
        if (participantView != null) {
            participantView.setVisibility(View.GONE);
            participantView.releaseSurfaceViewRenderer();
        }

        if (localView != null) {
            localView.setVisibility(View.GONE);
            localView.releaseSurfaceViewRenderer();
        }

        super.onDestroy();
    }
```

> `java.lang.IllegalStateException: This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.`
> If you face this error at Runtime, Include these lines in `theme.xml` file.
> `<item name="windowActionBar">false</item>`
> `<item name="windowNoTitle">true</item>`

## App Demo
Tadaa!! Our app is ready. Easy, isn't it?
Install and run the app on two different devices and make sure that they are connected to the internet.

> This app only supports 2 participants, it does not manage more than 2 participants. If you want to handle more than 2 participant then checkout our Group call example [here](https://github.com/videosdk-live/videosdk-rtc-android-java-sdk-example).

## Conclusion

- In this blog, we have learned what video SDK is, how to obtain an access token from the Video SDK [dashboard](https://app.videosdk.live/api-keys), and how to create a one to one video call app with the Video SDK.
- Go ahead and create advanced features like screen-sharing,chat and others.Browse Our [documentation](https://docs.videosdk.live/).
- To see the full implementation of the app, check out this [GitHub repository](https://github.com/videosdk-live/videosdk-rtc-android-java-sdk-example/tree/one-to-one-demo).
- If you face any problem or have questions, Please join our [Discord community](https://discord.gg/Gpmj6eCq5u).

## More Android Resources

- [Android One-To-One video calling app with Kotlin](https://www.videosdk.live/blog/1-to-1-video-chat-app-on-android-using-videosdk)
- [Android Interactive Live Streaming App with Kotlin](https://www.videosdk.live/blog/android-live-streaming)
- [Android Video Conferencing App with Kotlin example code](https://github.com/videosdk-live/videosdk-rtc-android-kotlin-sdk-example)
- [Android Interactive Live Streaming App with Kotlin example code](https://github.com/videosdk-live/videosdk-hls-android-kotlin-example)
- [Android Interactive Live Streaming App with Java example code](https://github.com/videosdk-live/videosdk-hls-android-java-example)
- [Android Video Conferencing App with Java example code](https://github.com/videosdk-live/videosdk-rtc-android-java-sdk-example)