Skip to content

Instantly share code, notes, and snippets.

@MikluhaMaclay
Last active October 27, 2021 10:28
Show Gist options
  • Save MikluhaMaclay/32a2ecaac25c022eb915588ede5a4590 to your computer and use it in GitHub Desktop.
Save MikluhaMaclay/32a2ecaac25c022eb915588ede5a4590 to your computer and use it in GitHub Desktop.
Participants pager
@OptIn(ExperimentalPagerApi::class)
@Composable
fun ParticipantsPager(participants: List<Participant>) {
if (participants.isEmpty()) {
return
}
val participantPages = participants.chunked(4)
var fullScreenParticipantId by rememberSaveable {
mutableStateOf<String?>(null)
}
val fullScreenLayoutCoordinates = remember {
mutableStateOf<LayoutCoordinates?>(null)
}
val layoutCoordinates = remember {
mutableStateOf<LayoutCoordinates?>(null)
}
Box(Modifier.onGloballyPositioned {
layoutCoordinates.value = it
}) {
HorizontalPager(
count = participantPages.size, modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
) { page ->
val participantsPage = participantPages[page]
ParticipantsPage(participantsPage, onDoubleTap = { participant, layoutCoordinates ->
fullScreenParticipantId = participant.id
fullScreenLayoutCoordinates.value = layoutCoordinates
}, onGloballyPositioned = { participant, layoutCoordinates ->
if (fullScreenParticipantId == participant.id) {
fullScreenLayoutCoordinates.value = layoutCoordinates
}
})
}
if (fullScreenParticipantId != null) {
FullScreenParticipant(
participant = participants.find { it.id == fullScreenParticipantId }!!,
coordinates = fullScreenLayoutCoordinates.value,
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
detectTapGestures(onDoubleTap = {
fullScreenLayoutCoordinates.value = null
fullScreenParticipantId = null
})
},
parentCoordinates = layoutCoordinates.value
)
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ParticipantsPage(
participantsPage: List<Participant>,
onDoubleTap: (participant: Participant, layoutCoordinates: LayoutCoordinates?) -> Unit,
onGloballyPositioned: (participant: Participant, layoutCoordinates: LayoutCoordinates) -> Unit
) {
LazyVerticalGrid(
cells = GridCells.Fixed(2), modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.padding(top = 74.dp, bottom = 79.dp)
) {
items(participantsPage.size) {
val participant = participantsPage[it]
val paddingStart = if (it.isEven()) 16 else 2
val paddingEnd = if (!it.isEven()) 16 else 2
val position = remember {
mutableStateOf<LayoutCoordinates?>(null)
}
Participant(
participant = participant,
modifier = Modifier
.pointerInput(Unit) {
detectTapGestures(
onDoubleTap = {
onDoubleTap(participant, position.value)
}
)
}
.fillParentMaxSize(0.5f)
.padding(start = paddingStart.dp, end = paddingEnd.dp, bottom = 5.dp)
.onGloballyPositioned { coordinates ->
position.value = coordinates
onGloballyPositioned(participant, coordinates)
}
)
}
}
}
@Composable
fun FullScreenParticipant(
participant: Participant,
coordinates: LayoutCoordinates?,
parentCoordinates: LayoutCoordinates?,
modifier: Modifier = Modifier
) {
val density = LocalDensity.current.density
var height = 0f
var width = 0f
if (coordinates != null) {
height = coordinates.size.height / density
width = coordinates.size.width / density
}
val parentHeight = parentCoordinates!!.size.height / density
val parentWidth = parentCoordinates.size.width / density
var x = 0f
var y = 0f
if (coordinates != null) {
x = coordinates.positionInRoot().x / density
y = coordinates.positionInRoot().y / density
}
val isVideoStarted = remember {
mutableStateOf<Boolean>(!participant.isVideoOn)
}
val heightToAnimate = if (isVideoStarted.value) parentHeight else height
val heightAnimation by animateDpAsState(heightToAnimate.dp)
val widthToAnimate = if (isVideoStarted.value) parentWidth else width
val widthAnimation by animateDpAsState(widthToAnimate.dp)
val xToAnimate = if (isVideoStarted.value) 0f else x
val yToAnimate = if (isVideoStarted.value) 0f else y
val xAnimation by animateDpAsState(xToAnimate.dp)
val yAnimation by animateDpAsState(yToAnimate.dp)
Box(modifier = modifier.fillMaxSize()) {
AndroidView(
factory = { context ->
PeerView(context)
},
update = { view ->
view.setRenderEventsListener(object : PeerView.PeerViewRenderEventsListener {
override fun onFirstFrameRender() {
isVideoStarted.value = true
}
})
when (participant) {
is LocalParticipant -> {
if (participant.isVideoOn) {
view.showVideoSource(participant.videoSource!!)
} else {
view.removeVideoSource()
}
}
is RemoteParticipant -> {
if (participant.isVideoOn) {
view.showVideoSource(participant.peer.sdkPeer)
} else {
view.removeVideoSource()
}
}
}
}, modifier = Modifier
.height(heightAnimation)
.width(widthAnimation)
.offset(x = xAnimation, y = yAnimation)
)
}
}
@Composable
fun Participant(participant: Participant, modifier: Modifier = Modifier) {
Card(
modifier = modifier,
backgroundColor = colorResource(R.color.bg_no_video_card),
shape = RoundedCornerShape(12.dp)
) {
Box {
AndroidView(
factory = { context ->
PeerView(context)
},
update = { view ->
when (participant) {
is LocalParticipant -> {
if (participant.isVideoOn) {
view.showVideoSource(participant.videoSource!!)
} else {
view.removeVideoSource()
}
}
is RemoteParticipant -> {
if (participant.isVideoOn) {
view.showVideoSource(participant.peer.sdkPeer)
} else {
view.removeVideoSource()
}
}
}
}, modifier = Modifier.fillMaxSize()
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment