Finish Android tutorial

This commit is contained in:
juancarmore 2024-07-24 18:34:46 +02:00
parent 495f780fa9
commit c3b9d3c5f8
11 changed files with 196 additions and 27 deletions

View File

@ -19,7 +19,7 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.participantName.setText("Participant %d".format((1..100).random()))
binding.participantName.setText("Participant%d".format((1..100).random()))
binding.joinButton.setOnClickListener {
navigateToRoomLayoutActivity()

View File

@ -0,0 +1,23 @@
package io.openvidu.android
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.livekit.android.room.Room
import io.openvidu.android.databinding.ParticipantItemBinding
class ParticipantAdapter(private val participantTracks: List<TrackInfo>, private val room: Room) :
RecyclerView.Adapter<ParticipantViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ParticipantViewHolder =
ParticipantViewHolder(
ParticipantItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
override fun onBindViewHolder(holder: ParticipantViewHolder, position: Int) {
val trackInfo = participantTracks[position]
holder.render(trackInfo, room)
}
override fun getItemCount(): Int = participantTracks.size
}

View File

@ -0,0 +1,28 @@
package io.openvidu.android
import androidx.recyclerview.widget.RecyclerView
import io.livekit.android.room.Room
import io.openvidu.android.databinding.ParticipantItemBinding
class ParticipantViewHolder(private val binding: ParticipantItemBinding) :
RecyclerView.ViewHolder(binding.root) {
private var used = false
fun render(trackInfo: TrackInfo, room: Room) {
val participantIdentity = if (trackInfo.isLocal) {
trackInfo.participantIdentity + " (You)"
} else {
trackInfo.participantIdentity
}
binding.identity.text = participantIdentity
if (!used) {
room.initVideoRenderer(binding.renderer)
used = true
}
trackInfo.track.addRenderer(binding.renderer)
}
}

View File

@ -9,6 +9,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO
@ -23,17 +24,26 @@ import io.livekit.android.events.RoomEvent
import io.livekit.android.events.collect
import io.livekit.android.room.Room
import io.livekit.android.room.track.VideoTrack
import io.livekit.android.util.flow
import io.openvidu.android.databinding.ActivityRoomLayoutBinding
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
data class TrackInfo(
val track: VideoTrack,
val participantIdentity: String,
val isLocal: Boolean = false
)
class RoomLayoutActivity : AppCompatActivity() {
private lateinit var binding: ActivityRoomLayoutBinding
private lateinit var participantAdapter: ParticipantAdapter
private lateinit var APPLICATION_SERVER_URL: String
private lateinit var LIVEKIT_URL: String
private lateinit var room: Room
private val participantTracks: MutableList<TrackInfo> = mutableListOf()
private val client = HttpClient(CIO) {
expectSuccess = true
@ -47,22 +57,33 @@ class RoomLayoutActivity : AppCompatActivity() {
binding = ActivityRoomLayoutBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.loader.visibility = View.VISIBLE
binding.leaveButton.setOnClickListener {
leaveRoom()
}
APPLICATION_SERVER_URL = intent.getStringExtra("serverUrl") ?: ""
LIVEKIT_URL = intent.getStringExtra("livekitUrl") ?: ""
// Create Room object.
room = LiveKit.create(applicationContext)
// Setup the video renderer
room.initVideoRenderer(binding.renderer)
initRecyclerView()
requestNeededPermissions { connectToRoom() }
}
private fun initRecyclerView() {
participantAdapter = ParticipantAdapter(participantTracks, room)
binding.participants.layoutManager = LinearLayoutManager(this)
binding.participants.adapter = participantAdapter
}
private fun connectToRoom() {
val participantName = intent.getStringExtra("participantName") ?: "Participant 1"
val roomName = intent.getStringExtra("roomName") ?: "Test Room"
binding.roomName.text = roomName
lifecycleScope.launch {
// Setup event handling.
launch {
@ -86,6 +107,24 @@ class RoomLayoutActivity : AppCompatActivity() {
val localParticipant = room.localParticipant
localParticipant.setMicrophoneEnabled(true)
localParticipant.setCameraEnabled(true)
// Add local video track to the participantTracks list.
launch {
localParticipant::videoTrackPublications.flow
.collect { publications ->
val videoTrack = publications.firstOrNull()?.second as? VideoTrack
if (videoTrack != null) {
participantTracks.add(
0,
TrackInfo(videoTrack, participantName, true)
)
participantAdapter.notifyItemInserted(0)
}
}
}
binding.loader.visibility = View.GONE
} catch (e: Exception) {
println("There was an error connecting to the room: ${e.message}")
Toast.makeText(this@RoomLayoutActivity, "Failed to join room", Toast.LENGTH_SHORT)
@ -99,26 +138,22 @@ class RoomLayoutActivity : AppCompatActivity() {
val track = event.track
if (track is VideoTrack) {
attachVideo(track)
participantTracks.add(TrackInfo(track, event.participant.identity!!.value))
participantAdapter.notifyItemInserted(participantTracks.size - 1)
}
}
private fun attachVideo(videoTrack: VideoTrack) {
videoTrack.addRenderer(binding.renderer)
binding.progress.visibility = View.GONE
}
private fun onTrackUnsubscribed(event: RoomEvent.TrackUnsubscribed) {
val track = event.track
if (track is VideoTrack) {
detachVideo(track)
}
}
val index = participantTracks.indexOfFirst { it.track.sid == track.sid }
private fun detachVideo(videoTrack: VideoTrack) {
videoTrack.removeRenderer(binding.renderer)
binding.progress.visibility = View.VISIBLE
if (index != -1) {
participantTracks.removeAt(index)
participantAdapter.notifyItemRemoved(index)
}
}
}
private fun leaveRoom() {

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/participantBackground" />
<corners android:radius="5dp" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/identityBackground" />
<corners
android:bottomRightRadius="5dp"
android:topLeftRadius="5dp" />
</shape>

View File

@ -49,7 +49,8 @@
android:inputType="text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/participantLabel" />
app:layout_constraintTop_toBottomOf="@+id/participantLabel"
tools:text="Participant1" />
<TextView
android:id="@+id/roomLabel"

View File

@ -1,19 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<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"
android:padding="20dp"
tools:context=".RoomLayoutActivity">
<io.livekit.android.renderer.SurfaceViewRenderer
android:id="@+id/renderer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progress"
<TextView
android:id="@+id/roomName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
android:layout_marginStart="16dp"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Test Room" />
</FrameLayout>
<Button
android:id="@+id/leaveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:backgroundTint="@color/leaveRoomButton"
android:text="@string/leave_room"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/loader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/participants"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/roomName" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/rounded_corner_background"
android:clipToOutline="true">
<io.livekit.android.renderer.TextureViewRenderer
android:id="@+id/renderer"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="3:4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/identity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rounded_corner_text"
android:paddingHorizontal="10dp"
android:textColor="@color/textColor"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Participant1" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -6,4 +6,7 @@
<color name="colorAccent">#0087A9</color>
<color name="backgroundColor">#4D4D4D</color>
<color name="textColor">#4D4D4D</color>
<color name="leaveRoomButton">#F24949</color>
<color name="participantBackground">#2F2F2F</color>
<color name="identityBackground">#B8B8B8</color>
</resources>

View File

@ -8,4 +8,5 @@
<string name="application_server_url">Application Server URL</string>
<string name="livekit_url">LiveKit URL</string>
<string name="settings">Settings</string>
<string name="leave_room">Leave Room</string>
</resources>