From 2fd5c8f9fa432ceba478365ccc05390e3a3e8106 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Sat, 27 Jul 2024 13:48:38 +0200 Subject: [PATCH] Add comments and README.md file to Android tutorial --- application-client/openvidu-android/README.md | 21 ++++++++++ .../java/io/openvidu/android/MainActivity.kt | 8 ++++ .../openvidu/android/ParticipantViewHolder.kt | 1 + .../io/openvidu/android/RoomLayoutActivity.kt | 39 +++++++++++++++---- 4 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 application-client/openvidu-android/README.md diff --git a/application-client/openvidu-android/README.md b/application-client/openvidu-android/README.md new file mode 100644 index 00000000..8754147d --- /dev/null +++ b/application-client/openvidu-android/README.md @@ -0,0 +1,21 @@ +# Basic Android + +Basic client application built for Android using Kotlin. It internally uses [livekit-client-sdk-android](https://docs.livekit.io/client-sdk-js/). + +For further information, check the [tutorial documentation](https://livekit-tutorials.openvidu.io/tutorials/application-client/android/). + +## Prerequisites + +- [Android Studio](https://developer.android.com/studio) + +## Run + +1. Download repository + +```bash +git clone https://github.com/OpenVidu/openvidu-livekit-tutorials.git +``` + +2. Open Android Studio and import the project `openvidu-livekit-tutorials/application-client/openvidu-android` + +3. Run the application in an emulator or a physical device by clicking the `Run` button in Android Studio. Check out the [official documentation](https://developer.android.com/studio/run) for further information. diff --git a/application-client/openvidu-android/app/src/main/java/io/openvidu/android/MainActivity.kt b/application-client/openvidu-android/app/src/main/java/io/openvidu/android/MainActivity.kt index c867d42b..249de477 100644 --- a/application-client/openvidu-android/app/src/main/java/io/openvidu/android/MainActivity.kt +++ b/application-client/openvidu-android/app/src/main/java/io/openvidu/android/MainActivity.kt @@ -3,6 +3,7 @@ package io.openvidu.android import android.content.Intent import android.os.Bundle import android.view.LayoutInflater +import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import io.openvidu.android.databinding.ActivityMainBinding @@ -11,6 +12,7 @@ import io.openvidu.android.databinding.DialogSettingsBinding class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding + // Configure this variables with correct URLs depending on your deployment private var applicationServerUrl = "https://{YOUR-LAN-IP}.openvidu-local.dev:6443/" private var livekitUrl = "wss://{YOUR-LAN-IP}.openvidu-local.dev:7443/" @@ -43,11 +45,17 @@ class MainActivity : AppCompatActivity() { intent.putExtra("serverUrl", applicationServerUrl) intent.putExtra("livekitUrl", livekitUrl) startActivity(intent) + } else { + Toast.makeText(this, "Please fill in all fields", Toast.LENGTH_SHORT).show() } binding.joinButton.isEnabled = true } + /** + * This dialog allows to change the LiveKit URL and the application server URL + * from the application itself. This is useful for development purposes. + */ private fun showSettingsDialog() { val dialogBinding = DialogSettingsBinding.inflate(LayoutInflater.from(this)) diff --git a/application-client/openvidu-android/app/src/main/java/io/openvidu/android/ParticipantViewHolder.kt b/application-client/openvidu-android/app/src/main/java/io/openvidu/android/ParticipantViewHolder.kt index e6aadcb7..3c0cbd62 100644 --- a/application-client/openvidu-android/app/src/main/java/io/openvidu/android/ParticipantViewHolder.kt +++ b/application-client/openvidu-android/app/src/main/java/io/openvidu/android/ParticipantViewHolder.kt @@ -18,6 +18,7 @@ class ParticipantViewHolder(private val binding: ParticipantItemBinding) : binding.identity.text = participantIdentity + // Only initialize the renderer once if (!used) { room.initVideoRenderer(binding.renderer) used = true diff --git a/application-client/openvidu-android/app/src/main/java/io/openvidu/android/RoomLayoutActivity.kt b/application-client/openvidu-android/app/src/main/java/io/openvidu/android/RoomLayoutActivity.kt index e3fe2fc8..f66dd9c0 100644 --- a/application-client/openvidu-android/app/src/main/java/io/openvidu/android/RoomLayoutActivity.kt +++ b/application-client/openvidu-android/app/src/main/java/io/openvidu/android/RoomLayoutActivity.kt @@ -65,10 +65,12 @@ class RoomLayoutActivity : AppCompatActivity() { APPLICATION_SERVER_URL = intent.getStringExtra("serverUrl") ?: "" LIVEKIT_URL = intent.getStringExtra("livekitUrl") ?: "" - // Create Room object. + // Create Room object room = LiveKit.create(applicationContext) initRecyclerView() + + // Check for audio and camera permissions before connecting to the room requestNeededPermissions { connectToRoom() } } @@ -79,17 +81,20 @@ class RoomLayoutActivity : AppCompatActivity() { } private fun connectToRoom() { - val participantName = intent.getStringExtra("participantName") ?: "Participant 1" + // Get the room name and participant name from the intent + val participantName = intent.getStringExtra("participantName") ?: "Participant1" val roomName = intent.getStringExtra("roomName") ?: "Test Room" binding.roomName.text = roomName lifecycleScope.launch { - // Setup event handling. + // Specify the actions when events take place in the room launch { room.events.collect { event -> when (event) { + // On every new Track received... is RoomEvent.TrackSubscribed -> onTrackSubscribed(event) + // On every new Track destroyed... is RoomEvent.TrackUnsubscribed -> onTrackUnsubscribed(event) else -> {} } @@ -97,18 +102,18 @@ class RoomLayoutActivity : AppCompatActivity() { } try { - // Get token from server. + // Get token from your application server with the room name and participant name val token = getToken(roomName, participantName) - // Connect to server. + // Connect to the room with the LiveKit URL and the token room.connect(LIVEKIT_URL, token) - // Turn on audio/video recording. + // Publish your camera and microphone val localParticipant = room.localParticipant localParticipant.setMicrophoneEnabled(true) localParticipant.setCameraEnabled(true) - // Add local video track to the participantTracks list. + // Add local video track to the participantTracks list launch { localParticipant::videoTrackPublications.flow .collect { publications -> @@ -137,6 +142,7 @@ class RoomLayoutActivity : AppCompatActivity() { private fun onTrackSubscribed(event: RoomEvent.TrackSubscribed) { val track = event.track + // If the track is a video track, add it to the participantTracks list if (track is VideoTrack) { participantTracks.add(TrackInfo(track, event.participant.identity!!.value)) participantAdapter.notifyItemInserted(participantTracks.size - 1) @@ -146,6 +152,7 @@ class RoomLayoutActivity : AppCompatActivity() { private fun onTrackUnsubscribed(event: RoomEvent.TrackUnsubscribed) { val track = event.track + // If the track is a video track, remove it from the participantTracks list if (track is VideoTrack) { val index = participantTracks.indexOfFirst { it.track.sid == track.sid } @@ -157,8 +164,11 @@ class RoomLayoutActivity : AppCompatActivity() { } private fun leaveRoom() { + // Leave the room by calling 'disconnect' method over the Room object room.disconnect() + client.close() + // Go back to the previous activity. finish() } @@ -173,7 +183,7 @@ class RoomLayoutActivity : AppCompatActivity() { registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grants -> var hasDenied = false - // Check if any permissions weren't granted. + // Check if any permissions weren't granted for (grant in grants.entries) { if (!grant.value) { Toast.makeText(this, "Missing permission: ${grant.key}", Toast.LENGTH_SHORT) @@ -203,6 +213,19 @@ class RoomLayoutActivity : AppCompatActivity() { } } + /** + * -------------------------------------------- + * GETTING A TOKEN FROM YOUR APPLICATION SERVER + * -------------------------------------------- + * The method below request the creation of a token to + * your application server. This prevents the need to expose + * your LiveKit API key and secret to the client side. + * + * In this sample code, there is no user control at all. Anybody could + * access your application server endpoints. In a real production + * environment, your application server must identify the user to allow + * access to the endpoints. + */ private suspend fun getToken(roomName: String, participantName: String): String { val response = client.post(APPLICATION_SERVER_URL + "token") { contentType(ContentType.Application.Json)