How to Build HTTP Live Streaming Android App in Java?
Tags: android, java, beginners, tutorial
Published: 2023-07-05 04:30:00

### Introduction
The [HTTP Live Streaming](https://www.videosdk.live/blog/what-is-http-live-streaming)(HLS) feature has gained tremendous popularity in recent times. It offers content creators, businesses, and developers unique opportunities to connect with their audience in real time.

In this tutorial, You will learn how to integrate interactive live streaming in your Android app in Java using VideoSDK.

The [VideoSDK ](https://www.videosdk.live/blog)is a powerful tool that allows you to incorporate real-time interactive streaming capabilities into your applications. With interactive live streaming, you can engage your users in dynamic and immersive experiences, such as live events, gaming broadcasts, virtual classrooms, and more.

By the end of this tutorial, you will have acquired valuable skills in integrating interactive live streaming into your Android app, empowering you to create engaging and immersive experiences for your users. So, let's embark on this journey and unlock the potential of interactive live streaming with VideoSDK!

## 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?gclid=Cj0KCQjw4s-kBhDqARIsAN-ipH1LHN3BHD8E8boP9bhHZMQKve-7beXh5xlozj9hspl3Oc1FmGduOd8aAlm7EALw_wcB&gclsrc=aw.ds) 3.0 or later.
- Android SDK API Level 21 or higher.
- A mobile device that runs Android 5.0 or later.

> **INFO**
> One should have a Video SDK account to generate a token. Visit VideoSDK [dashboard](https://app.videosdk.live/api-keys) to generate a token.

## Getting Started with the Code!
Follow the steps to create the necessary environment to add HTTP Live Streaming in your app. Also, you can find the code sample for quickstart [here](https://github.com/videosdk-live/quickstart/tree/main/android-hls).

### Create a new Android Project
For a new project in Android Studio, create a Phone and Tablet Android project with an Empty Activity.

### VideoSDK Android Quick Start New Project

> **CAUTION** 
> After creating the project, Android Studio automatically starts gradle sync. Ensure that the sync succeeds before you continue.

### Integrate Video SDK
**Maven Central**

```js
settings.gradle

dependencyResolutionManagement {
  repositories {
    // ...
    google()
    mavenCentral()
    maven { url "https://maven.aliyun.com/repository/jcenter" }
  }
}
```

> **NOTE** 
> You can use imports with Maven Central after rtc-android-sdk version 0.1.12. Whether on Maven or Jitpack, the same version numbers always refer to the same SDK.

Add the following dependency in your app's `app/build.gradle`.

```js
app/build.gradle
dependencies {implementation 'live.videosdk:rtc-android-sdk:0.1.17'
// library to perform Network call to generate a meeting idimplementation 'com.amitshekhar.android:android-networking:1.0.2'
// other app dependencies}
```

> **INFO** 
> Android SDK compatible with `armeabi-v7a`, `arm64-v8a`, `x86_64` architectures. If you want to run the application in an emulator, choose ABI `x86_64` when creating a device.

### Add permissions to your project
In `/app/Manifests/AndroidManifest.xml`, add the following permissions after `</application>`.

```js
AndroidManifest.xml

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
```

> **NOTE** 
> 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.

### Structure of the project
Your project structure should look like this:

```js
app
   ├── java
   │    ├── packagename
   │         ├── JoinActivity
   │         ├── MeetingActivity
   │         ├── SpeakerAdapter
   │         ├── SpeakerFragment
   |         ├── ViewerFragment
   ├── res
   │    ├── layout
   │    │    ├── activity_join.xml
   │    │    ├── activity_meeting.xml
   |    |    ├── fragment_speaker.xml
   |    |    ├── fragment_viewer.xml
   │    │    ├── item_remote_peer.xml
```

> **NOTE** 
> You have to set `JoinActivity` as the Launcher activity.

## 4 steps to build Android HTTP Live Streaming App with VideoSDK
### Step 1: Creating Joining Screen
Create a new Activity named JoinActivity.

**Creating UI**
The joining screen includes:

- Create Button: Creates a new meeting.
- TextField for Meeting Id: Contains the meeting Id you want to join.
- Join as Host Button: Joins the meeting as the host with the provided meetingId.
- Join as Viewer Button: Joins the meeting as a viewer with the provided meetingId.

In the `/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"
    android:id="@+id/createorjoinlayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:gravity="center"
    android:orientation="vertical">

    <Button
        android:id="@+id/btnCreateMeeting"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Create Meeting"
        android:textAllCaps="false" />

    <TextView
        android:id="@+id/tvText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingVertical="5sp"
        android:text="OR"
        android:textColor="@color/white"
        android:textSize="20sp" />

    <EditText
        android:id="@+id/etMeetingId"
        android:theme="@android:style/Theme.Holo"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:hint="Enter Meeting Id"
        android:textColor="@color/white"
        android:textColorHint="@color/white" />

    <Button
        android:id="@+id/btnJoinHostMeeting"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8sp"
        android:text="Join as Host"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/btnJoinViewerMeeting"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Join as Viewer"
        android:textAllCaps="false" />

</LinearLayout>
```

#### Integration of Create Meeting API​
- Create field `sampleToken` in `JoinActivity` that will hold the generated token from the Video SDK dashboard. This token will be used in the VideoSDK config as well as in 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 as Host `onClick` events, we will navigate to `MeetingActivity` with token, meetingId and mode as CONFERENCE.

- On Join Button as Viewer `onClick` events, we will navigate to `MeetingActivity` with token, meetingId and mode as Viewer.

```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 btnJoinHost = findViewById(R.id.btnJoinHostMeeting);
    final Button btnJoinViewer = findViewById(R.id.btnJoinViewerMeeting);
    final EditText etMeetingId = findViewById(R.id.etMeetingId);

    // create meeting and join as Host
    btnCreate.setOnClickListener(v -> createMeeting(sampleToken));

    // Join as Host
    btnJoinHost.setOnClickListener(v -> {
        Intent intent = new Intent(JoinActivity.this, MeetingActivity.class);
        intent.putExtra("token", sampleToken);
        intent.putExtra("meetingId", etMeetingId.getText().toString().trim());
        intent.putExtra("mode", "CONFERENCE");
        startActivity(intent);
    });

    // Join as Viewer
    btnJoinViewer.setOnClickListener(v -> {
        Intent intent = new Intent(JoinActivity.this, MeetingActivity.class);
        intent.putExtra("token", sampleToken);
        intent.putExtra("meetingId", etMeetingId.getText().toString().trim());
        intent.putExtra("mode", "VIEWER");
        startActivity(intent);
    });
  }

  private void createMeeting(String token) {
    // we will explore this method in the next step
  }
}
```

- For Create Button under `createMeeting` method, we will generate meetingId by calling API and navigate to `MeetingActivity` with token, generated meetingId and mode as `CONFERENCE`.

```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);
                  intent.putExtra("mode", "CONFERENCE");
                  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();
              }
          });
  }
}
```

> **NOTE**
> Don't get confused between Room and Meeting keywords, both are same thing.

- Our App is completely based on audio and video communication, 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 void checkSelfPermission(String permission, int requestCode) {
    if (ContextCompat.checkSelfPermission(this, permission) !=
            PackageManager.PERMISSION_GRANTED) {
      ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
    }
  }

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

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

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

```js
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    tools:context=".MeetingActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Creating a meeting for you"
        android:textColor="@color/white"
        android:textFontWeight="700"
        android:textSize="20sp" />

</RelativeLayout>
```

**Initializing the Meeting​**
After getting the token, meetigId, and mode from `JoinActivity`,

1. Initialize **Video SDK**.
2. Configure **Video SDK **with token.
3. Initialize the meeting with required params such as `meetingId`, `participantName`, `micEnabled`, `webcamEnabled`, `mode` and more.
4. Join the room with `meeting.join()` method.
5. Add `MeetingEventListener` for listening Meeting Join event.
6. Check mode of `localParticipant`, If the mode is **CONFERENCE**, We will replace mainLayout with `SpeakerFragment` otherwise replace with `ViewerFragment`.

```js
public class MeetingActivity extends AppCompatActivity {
  private Meeting meeting;

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

    final String meetingId = getIntent().getStringExtra("meetingId");
    String token = getIntent().getStringExtra("token");
    String mode = getIntent().getStringExtra("mode");
    String localParticipantName = "John Doe";
    boolean streamEnable = mode.equals("CONFERENCE");

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

    // Configuration VideoSDK with Token
    VideoSDK.config(token);

    // Initialize VideoSDK Meeting
    meeting = VideoSDK.initMeeting(
            MeetingActivity.this, meetingId, localParticipantName,
            streamEnable, streamEnable, null, mode, false, null);

    // join Meeting
    meeting.join();

    // if mode is CONFERENCE than replace mainLayout with SpeakerFragment otherwise with ViewerFragment
    meeting.addEventListener(new MeetingEventListener() {
        @Override
        public void onMeetingJoined() {
          if (meeting != null) {
            if (mode.equals("CONFERENCE")) {
              //pin the local partcipant
              meeting.getLocalParticipant().pin("SHARE_AND_CAM");
              getSupportFragmentManager()
                  .beginTransaction()
                  .replace(R.id.mainLayout, new SpeakerFragment(), "MainFragment")
                  .commit();
            } else if (mode.equals("VIEWER")) {
              getSupportFragmentManager()
                  .beginTransaction()
                  .replace(R.id.mainLayout, new ViewerFragment(), "viewerFragment")
                  .commit();
            }
          }
        }
      });
  }
  public Meeting getMeeting() {
      return meeting;
  }
}
```

### Step 3: Implement SpeakerView​
After successfully entering the meeting, it's time to render speaker's view and manage controls such as toggle webcam/mic, start/stop HLS and leave the meeting.

- Create a new fragment named `SpeakerFragment`.
- In `/app/res/layout/fragment_speaker.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:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".SpeakerFragment">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="8sp"
        android:paddingHorizontal="10sp">

        <TextView
            android:id="@+id/tvMeetingId"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="Meeting Id : "
            android:textColor="@color/white"
            android:textSize="18sp"
            android:layout_weight="3"/>

        <Button
            android:id="@+id/btnLeave"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="Leave"
            android:textAllCaps="false"
            android:layout_weight="1"/>

    </LinearLayout>

    <TextView
        android:id="@+id/tvHlsState"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Current HLS State : NOT_STARTED"
        android:textColor="@color/white"
        android:textSize="18sp" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvParticipants"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginVertical="10sp"
        android:layout_weight="1" />

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

        <Button
            android:id="@+id/btnHLS"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Start HLS"
            android:textAllCaps="false" />

        <Button
            android:id="@+id/btnWebcam"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="5sp"
            android:text="Toggle Webcam"
            android:textAllCaps="false" />

        <Button
            android:id="@+id/btnMic"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Toggle Mic"
            android:textAllCaps="false" />

    </LinearLayout>

</LinearLayout>
```

- Now, let's set listener for buttons that will allow the participant to toggle media.

```js
public class SpeakerFragment extends Fragment {

  private static Activity mActivity;
  private static Context mContext;
  private static Meeting meeting;
  private boolean micEnabled = true;
  private boolean webcamEnabled = true;
  private boolean hlsEnabled = false;
  private Button btnMic, btnWebcam, btnHls, btnLeave;
  private TextView tvMeetingId, tvHlsState;

  public SpeakerFragment() {
    // Required empty public constructor
  }

  @Override
  public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    mContext = context;
    if (context instanceof Activity) {
      mActivity = (Activity) context;
      // getting meeting object from Meeting Activity
      meeting = ((MeetingActivity) mActivity).getMeeting();
    }
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_speaker, container, false);
    btnMic = view.findViewById(R.id.btnMic);
    btnWebcam = view.findViewById(R.id.btnWebcam);
    btnHls = view.findViewById(R.id.btnHLS);
    btnLeave = view.findViewById(R.id.btnLeave);

    tvMeetingId = view.findViewById(R.id.tvMeetingId);
    tvHlsState = view.findViewById(R.id.tvHlsState);

    if (meeting != null) {
        tvMeetingId.setText("Meeting Id : " + meeting.getMeetingId());
        setActionListeners();
    }
    return view;
  }

  private void setActionListeners() {
    btnMic.setOnClickListener(v -> {
        if (micEnabled) {
          meeting.muteMic();
          Toast.makeText(mContext,"Mic Muted",Toast.LENGTH_SHORT).show();
        } else {
          meeting.unmuteMic();
          Toast.makeText(mContext,"Mic Enabled",Toast.LENGTH_SHORT).show();
        }
        micEnabled=!micEnabled;
    });

    btnWebcam.setOnClickListener(v -> {
        if (webcamEnabled) {
          meeting.disableWebcam();
          Toast.makeText(mContext,"Webcam Disabled",Toast.LENGTH_SHORT).show();
        } else {
          meeting.enableWebcam();
          Toast.makeText(mContext,"Webcam Enabled",Toast.LENGTH_SHORT).show();
        }
        webcamEnabled=!webcamEnabled;
    });

    btnLeave.setOnClickListener(v -> meeting.leave());

    btnHls.setOnClickListener(v -> {
      if (!hlsEnabled) {
        JSONObject config = new JSONObject();
        JSONObject layout = new JSONObject();
        JsonUtils.jsonPut(layout, "type", "SPOTLIGHT");
        JsonUtils.jsonPut(layout, "priority", "PIN");
        JsonUtils.jsonPut(layout, "gridSize", 4);
        JsonUtils.jsonPut(config, "layout", layout);
        JsonUtils.jsonPut(config, "orientation", "portrait");
        JsonUtils.jsonPut(config, "theme", "DARK");
        JsonUtils.jsonPut(config, "quality", "high");
        meeting.startHls(config);
      } else {
        meeting.stopHls();
      }
    });
  }
}
```

- After adding listeners for buttons, let's add `MeetingEventListener` to the meeting and remove all the listeners in `onDestroy()` method.

```js
public class SpeakerFragment extends Fragment {

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
    //...
    if (meeting != null) {
        //...
        // add Listener to the meeting
        meeting.addEventListener(meetingEventListener);
    }
    return view;
  }

  private final MeetingEventListener meetingEventListener = new MeetingEventListener() {
    @Override
    public void onMeetingLeft() {
      //unpin local participant
      meeting.getLocalParticipant().unpin("SHARE_AND_CAM");
      if (isAdded()) {
        Intent intents = new Intent(mContext, JoinActivity.class);
        intents.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intents);
        mActivity.finish();
      }
    }

    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    public void onHlsStateChanged(JSONObject HlsState) {
        if (HlsState.has("status")) {
          try {
            tvHlsState.setText("Current HLS State : " + HlsState.getString("status"));
            if (HlsState.getString("status").equals("HLS_STARTED")) {
                hlsEnabled=true;
                btnHls.setText("Stop HLS");
            }
            if (HlsState.getString("status").equals("HLS_STOPPED")) {
                hlsEnabled = false;
                btnHls.setText("Start HLS");
            }
          } catch (JSONException e) {
              e.printStackTrace();
          }
        }
    }
  };

  @Override
  public void onDestroy() {
    mContext = null;
    mActivity = null;
    if (meeting != null) {
        meeting.removeAllListeners();
        meeting = null;
    }
    super.onDestroy();
  }

}
```

- The next step is to render the speaker's view. With RecyclerView, we will display the list of participants who has joined the meeting as host.

> **INFO**
> - Here the participant's video is displayed using VideoView, but you may also use SurfaceViewRender for the same.
> - For VideoView, the SDK version should be 0.1.13 or higher.
> - To know more about VideoView, please visit [here](https://docs.videosdk.live/android/guide/video-and-audio-calling-api-sdk/render-media/display-video/understand-videoView-component)

- Create a new layout for the participant view named item_remote_peer.xml in the res/layout folder.

```js
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="200dp"
    android:background="@color/cardview_dark_background"
    tools:layout_height="200dp">

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="#99000000"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="4dp"
            android:textColor="@color/white" />

    </LinearLayout>

</FrameLayout>
```

- Create a recycler view adapter named SpeakerAdapter that will show the participant list. Create PeerViewHolder in the adapter which will extend RecyclerView.ViewHolder.

```js
public class SpeakerAdapter extends RecyclerView.Adapter<SpeakerAdapter.PeerViewHolder> {
  private List<Participant> participantList = new ArrayList<>();
  private final Meeting meeting;
  public SpeakerAdapter(Meeting meeting) {
    this.meeting = meeting;

    updateParticipantList();

    // adding Meeting Event listener to get the participant join/leave event in the meeting.
    meeting.addEventListener(new MeetingEventListener() {
      @Override
      public void onParticipantJoined(Participant participant) {
        // check participant join as Host/Speaker or not
        if (participant.getMode().equals("CONFERENCE")) {
          // pin the participant
          participant.pin("SHARE_AND_CAM");
          // add participant in participantList
          participantList.add(participant);
        }
        notifyDataSetChanged();
      }

      @Override
      public void onParticipantLeft(Participant participant) {
        int pos = -1;
        for (int i = 0; i < participantList.size(); i++) {
          if (participantList.get(i).getId().equals(participant.getId())) {
            pos = i;
            break;
          }
        }
        if(participantList.contains(participant)) {
          // unpin participant who left the meeting
          participant.unpin("SHARE_AND_CAM");
          // remove participant from the list
          participantList.remove(participant);
        }
        if (pos >= 0) {
            notifyItemRemoved(pos);
        }
      }
    });
  }

  private void updateParticipantList() {
    participantList = new ArrayList<>();

    // adding the local participant(You) to the list
    participantList.add(meeting.getLocalParticipant());

    // adding participants who join as Host/Speaker
    Iterator<Participant> participants = meeting.getParticipants().values().iterator();
    for (int i = 0; i < meeting.getParticipants().size(); i++) {
      final Participant participant = participants.next();
      if (participant.getMode().equals("CONFERENCE")) {
        // pin the participant
        participant.pin("SHARE_AND_CAM");
        // add participant in participantList
        participantList.add(participant);
      }
    }
  }

  @NonNull
  @Override
  public PeerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new PeerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_remote_peer, parent, false));
  }

  @Override
  public void onBindViewHolder(@NonNull PeerViewHolder holder, int position) {
    Participant participant = participantList.get(position);

    holder.tvName.setText(participant.getDisplayName());

    // adding the initial video stream for the participant into the 'VideoView'
    for (Map.Entry<String, Stream> entry : participant.getStreams().entrySet()) {
      Stream stream = entry.getValue();
      if (stream.getKind().equalsIgnoreCase("video")) {
        holder.participantView.setVisibility(View.VISIBLE);
        VideoTrack videoTrack = (VideoTrack) stream.getTrack();
        holder.participantView.addTrack(videoTrack);
        break;
      }
    }

    // add Listener to the participant which will update start or stop the video stream of that participant
    participant.addEventListener(new ParticipantEventListener() {
        @Override
        public void onStreamEnabled(Stream stream) {
          if (stream.getKind().equalsIgnoreCase("video")) {
            holder.participantView.setVisibility(View.VISIBLE);
            VideoTrack videoTrack = (VideoTrack) stream.getTrack();
            holder.participantView.addTrack(videoTrack);
          }
        }

        @Override
        public void onStreamDisabled(Stream stream) {
          if (stream.getKind().equalsIgnoreCase("video")) {
            holder.participantView.removeTrack();
            holder.participantView.setVisibility(View.GONE);
          }
        }
    });
  }

  @Override
  public int getItemCount() {
    return participantList.size();
  }

  static class PeerViewHolder extends RecyclerView.ViewHolder {
    // 'VideoView' to show Video Stream
    public VideoView participantView;
    public TextView tvName;
    public View itemView;

    PeerViewHolder(@NonNull View view) {
        super(view);
        itemView = view;
        tvName = view.findViewById(R.id.tvName);
        participantView = view.findViewById(R.id.participantView);
    }
  }
}
```

- Add this adapter to the `SpeakerFragment`

```js
 @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
    //...
    if (meeting != null) {
      //...
      final RecyclerView rvParticipants = view.findViewById(R.id.rvParticipants);
      rvParticipants.setLayoutManager(new GridLayoutManager(mContext, 2));
      rvParticipants.setAdapter(new SpeakerAdapter(meeting));
    }
}
```

### Step 4: Implement ViewerView​
When the host starts live streaming, The viewer will be able to see the live streaming.

To implement the player view, We're going to use `ExoPlayer`. It will be helpful to play hls stream.

But first, Let's add dependency into the project.

```js
dependencies {
  implementation 'com.google.android.exoplayer:exoplayer:2.18.5'
  // other app dependencies
  }
```

Create a new Fragment named `ViewerFragment`

#### Creating UI​
The Viewer Fragment will include :

- **TextView for Meeting Id** - The meeting Id that you joined will be displayed in this text view.
- **Leave Button** - This button will leave the meeting.
- **waitingLayout** - This is textView that will be shown when there is no active HLS.
- **StyledPlayerView** - This is mediaplayer which will display livestreaming.

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

```js
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:background="@color/black"
    tools:context=".ViewerFragment">

    <LinearLayout
        android:id="@+id/meetingLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingHorizontal="12sp"
        android:paddingVertical="5sp">

        <TextView
            android:id="@+id/meetingId"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="3"
            android:text="Meeting Id : "
            android:textColor="@color/white"
            android:textSize="20sp" />

        <Button
            android:id="@+id/btnLeave"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Leave" />

    </LinearLayout>

    <TextView
        android:id="@+id/waitingLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Waiting for host \n to start the live streaming"
        android:textColor="@color/white"
        android:textFontWeight="700"
        android:textSize="20sp"
        android:gravity="center"/>

    <com.google.android.exoplayer2.ui.StyledPlayerView
        android:id="@+id/player_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"
        app:resize_mode="fixed_width"
        app:show_buffering="when_playing"
        app:show_subtitle_button="false"
        app:use_artwork="false"
        app:show_next_button="false"
        app:show_previous_button="false"
        app:use_controller="true"
        android:layout_below="@id/meetingLayout"/>

</RelativeLayout>
```

### Initialize player and Playing HLS stream
Initialize player and play the HLS when the HLS state is `HLS_PLAYABLE`, and release it when the HLS state is `HLS_STOPPED`. Whenever the meeting HLS state changes, the event `onHlsStateChanged` will be triggered.

```js
public class ViewerFragment extends Fragment {

  private Meeting meeting;
  protected StyledPlayerView playerView;
  private TextView waitingLayout;
  protected @Nullable
  ExoPlayer player;

  private DefaultHttpDataSource.Factory dataSourceFactory;
  private boolean startAutoPlay=true;
  private String downStreamUrl = "";
  private static Activity mActivity;
  private static Context mContext;

  public ViewerFragment() {
      // Required empty public constructor
  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_viewer, container, false);

    playerView = view.findViewById(R.id.player_view);

    waitingLayout = view.findViewById(R.id.waitingLayout);
    if(meeting != null) {
      // set MeetingId to TextView
      ((TextView) view.findViewById(R.id.meetingId)).setText("Meeting Id : " + meeting.getMeetingId());
      // leave the meeting on btnLeave click
      ((Button) view.findViewById(R.id.btnLeave)).setOnClickListener(v -> meeting.leave());
      // add listener to meeting
      meeting.addEventListener(meetingEventListener);
    }
    return view;
  }


  @Override
  public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    mContext = context;
    if (context instanceof Activity) {
      mActivity = (Activity) context;
      // get meeting object from MeetingActivity
      meeting = ((MeetingActivity) mActivity).getMeeting();
    }
  }

  private final MeetingEventListener meetingEventListener = new MeetingEventListener() {

      @Override
      public void onMeetingLeft() {
        if (isAdded()) {
          Intent intents = new Intent(mContext, JoinActivity.class);
          intents.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                  | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
          startActivity(intents);
          mActivity.finish();
        }
      }

      @RequiresApi(api = Build.VERSION_CODES.P)
      @Override
      public void onHlsStateChanged(JSONObject HlsState) {
        if (HlsState.has("status")) {
          try {
            if (HlsState.getString("status").equals("HLS_PLAYABLE") && HlsState.has("downstreamUrl")) {
              downStreamUrl = HlsState.getString("downstreamUrl");
              waitingLayout.setVisibility(View.GONE);
              playerView.setVisibility(View.VISIBLE);
              // initialize player
              initializePlayer();
            }
            if (HlsState.getString("status").equals("HLS_STOPPED")) {
              // release the player
              releasePlayer();
              downStreamUrl = null;
              waitingLayout.setText("Host has stopped \n the live streaming");
              waitingLayout.setVisibility(View.VISIBLE);
              playerView.setVisibility(View.GONE);
            }
          } catch (JSONException e) {
              e.printStackTrace();
          }
        }
    }
  };

  protected void initializePlayer() {
    if (player == null) {
      dataSourceFactory = new DefaultHttpDataSource.Factory();
      HlsMediaSource mediaSource = new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(
              MediaItem.fromUri(Uri.parse(this.downStreamUrl)));
      ExoPlayer.Builder playerBuilder =
              new ExoPlayer.Builder(/* context= */ mContext);
      player = playerBuilder.build();
      // auto play when player is ready
      player.setPlayWhenReady(startAutoPlay);
      player.setMediaSource(mediaSource);
      // if you want display setting for player then remove this line
      playerView.findViewById(com.google.android.exoplayer2.ui.R.id.exo_settings).setVisibility(View.GONE);
      playerView.setPlayer(player);
    }
    player.prepare();
  }

  protected void releasePlayer() {
    if (player != null) {
      player.release();
      player = null;
      dataSourceFactory = null;
      playerView.setPlayer(/* player= */ null);
    }
  }

  @Override
  public void onDestroy() {
    mContext = null;
    mActivity = null;
    downStreamUrl = null;
    releasePlayer();
    if (meeting != null) {
      meeting.removeAllListeners();
      meeting = null;
    }
    super.onDestroy();
  }

}
```

## Final Output
We're done with the implementation of a customized video chat/calling app in Android using VideoSDK and Java. To explore more features, go through the Basic and Advanced features.

## Conclusion
Congratulations! By following this tutorial, you have successfully integrated interactive live streaming capabilities into your Android app using VideoSDK. Let's recap what you have accomplished:

- Created a user-friendly joining screen that allows users to enter a meeting ID and choose to join as a host or viewer.
- Implemented the necessary logic to generate meeting IDs and handle different roles (host/viewer).
- Developed a meeting screen where users can participate in HTTP live-streaming sessions, engage with the stream, chat with other participants, and have real-time discussions.

## More Android Resources

- [Android Live Streaming App using Kotlin](https://www.videosdk.live/blog/android-live-streaming)
- [Build Android Video Calling App - docs](https://docs.videosdk.live/android/guide/video-and-audio-calling-api-sdk/concept-and-architecture)
- [Build Android Video Calling App using Android Studio and Video SDK](https://youtu.be/Kj7jS3dbJFA)
- [quickstart/android-rtc](https://github.com/videosdk-live/quickstart/tree/main/android-rtc)
- [quickstart/android-hls](https://github.com/videosdk-live/quickstart/tree/main/android-hls)
- [videosdk-rtc-android-java-sdk-example](https://github.com/videosdk-live/videosdk-rtc-android-java-sdk-example)
- [videosdk-rtc-android-kotlin-sdk-example](https://github.com/videosdk-live/videosdk-rtc-android-kotlin-sdk-example)
- [videosdk-hls-android-java-example](https://github.com/videosdk-live/videosdk-hls-android-java-example)
- [videosdk-hls-android-kotlin-example](https://github.com/videosdk-live/videosdk-hls-android-kotlin-example)