Created
October 25, 2021 06:41
-
-
Save rohitjakhar/faabeb7772a5ddaa04782245fc983763 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.parrychat.android.ui.call | |
import android.Manifest | |
import android.annotation.SuppressLint | |
import android.os.Bundle | |
import android.util.Log | |
import android.view.LayoutInflater | |
import android.view.MotionEvent | |
import android.view.SurfaceView | |
import android.view.View | |
import android.view.ViewGroup | |
import android.view.WindowManager | |
import android.view.animation.AnimationUtils | |
import androidx.fragment.app.Fragment | |
import androidx.fragment.app.viewModels | |
import androidx.lifecycle.lifecycleScope | |
import androidx.navigation.fragment.findNavController | |
import androidx.navigation.fragment.navArgs | |
import androidx.recyclerview.widget.LinearLayoutManager | |
import coil.load | |
import com.google.android.flexbox.FlexDirection | |
import com.google.android.flexbox.FlexboxLayoutManager | |
import com.google.android.material.bottomsheet.BottomSheetDialog | |
import com.parrychat.android.R | |
import com.parrychat.android.data.model.HostProfileModel | |
import com.parrychat.android.data.model.MessageModel | |
import com.parrychat.android.data.model.MessageType | |
import com.parrychat.android.data.model.UserProfileModel | |
import com.parrychat.android.databinding.DialogStickersBinding | |
import com.parrychat.android.databinding.FragmentOngoingCallBinding | |
import com.parrychat.android.ui.chat.StickerAdapter | |
import com.parrychat.android.util.NetworkResponse | |
import com.parrychat.android.util.autoCleaned | |
import com.vmadalin.easypermissions.EasyPermissions | |
import com.vmadalin.easypermissions.annotations.AfterPermissionGranted | |
import dagger.hilt.android.AndroidEntryPoint | |
import io.agora.rtc.Constants.CHANNEL_PROFILE_COMMUNICATION | |
import io.agora.rtc.IRtcEngineEventHandler | |
import io.agora.rtc.RtcEngine | |
import io.agora.rtc.video.VideoCanvas | |
import io.agora.rtc.video.VideoEncoderConfiguration | |
import io.agora.rtc.video.VideoEncoderConfiguration.VD_640x480 | |
import io.agora.rtm.RtmChannel | |
import io.agora.rtm.RtmClient | |
import kotlinx.coroutines.ExperimentalCoroutinesApi | |
import kotlinx.coroutines.delay | |
import kotlinx.coroutines.flow.collectLatest | |
@AndroidEntryPoint | |
class OngoingCallFragment : Fragment() { | |
private var binding: FragmentOngoingCallBinding by autoCleaned() | |
private lateinit var hostProfileModel: HostProfileModel | |
private lateinit var userProfileModel: UserProfileModel | |
private val viewModel: OngoingCallViewModel by viewModels() | |
private val navArgs: OngoingCallFragmentArgs by navArgs() | |
private val mAdapter: OngoingCallAdapter by lazy { | |
OngoingCallAdapter { | |
if (userProfileModel.userCoin > it.giftPrice) { | |
stickerDialog.dismiss() | |
viewModel.sendMessage( | |
messageModel = MessageModel( | |
profileImage = hostProfileModel.hostProfileImage, | |
type = MessageType.STICKER, | |
mediaUrl = it.giftImage, | |
giftPrice = it.giftPrice | |
), | |
channelId = navArgs.channelId | |
) | |
viewModel.deductCoin(it.giftPrice) | |
viewLifecycleOwner.lifecycleScope.launchWhenStarted { | |
val animation = | |
AnimationUtils.loadAnimation(requireContext(), R.anim.amin_sticker_send) | |
binding.ivStickerSent.visibility = View.VISIBLE | |
binding.ivStickerSent.load(it.giftImage) | |
binding.ivStickerSent.animation = animation | |
delay(1500) | |
binding.ivStickerSent.visibility = View.GONE | |
animation.reset() | |
} | |
} else { | |
// TODO: Show dialog for less coin and buy coin | |
} | |
} | |
} | |
val stickerDialog by lazy { BottomSheetDialog(requireContext()) } | |
private val stickerAdapter: StickerAdapter by lazy { | |
StickerAdapter { | |
if (userProfileModel.userCoin > it.price) { | |
stickerDialog.dismiss() | |
viewModel.sendMessage( | |
messageModel = MessageModel( | |
profileImage = hostProfileModel.hostProfileImage, | |
type = MessageType.STICKER, | |
mediaUrl = it.image, | |
giftPrice = it.price | |
), | |
channelId = navArgs.channelId | |
) | |
viewModel.deductCoin(it.price) | |
viewLifecycleOwner.lifecycleScope.launchWhenStarted { | |
val animation = | |
AnimationUtils.loadAnimation(requireContext(), R.anim.amin_sticker_send) | |
binding.ivStickerSent.visibility = View.VISIBLE | |
binding.ivStickerSent.load(it.image) | |
binding.ivStickerSent.animation = animation | |
delay(1500) | |
binding.ivStickerSent.visibility = View.GONE | |
animation.reset() | |
} | |
} else { | |
// TODO: Show dialog for less coin and buy coin | |
} | |
} | |
} | |
private val stickerBinding: DialogStickersBinding by lazy { | |
DialogStickersBinding.inflate(layoutInflater) | |
} | |
private var dX: Float = 0F | |
private var dY: Float = 0F | |
private var lastAction: Int = 0 | |
private lateinit var mRtcChannel: RtmChannel | |
private lateinit var mRtcClient: RtmClient | |
private val mLocalView by lazy { | |
SurfaceView(requireContext()) | |
} | |
private val mRtcEngineEventHandler = | |
object : IRtcEngineEventHandler() { | |
override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) { | |
super.onJoinChannelSuccess(channel, uid, elapsed) | |
Log.d("rohit", "user joind channel: $uid") | |
requireActivity().runOnUiThread { | |
setupRemoteVideo(uid) | |
} | |
} | |
override fun onUserOffline(uid: Int, reason: Int) { | |
super.onUserOffline(uid, reason) | |
} | |
override fun onFirstRemoteVideoFrame(uid: Int, width: Int, height: Int, elapsed: Int) { | |
super.onFirstRemoteVideoFrame(uid, width, height, elapsed) | |
requireActivity().runOnUiThread { | |
setupRemoteVideo(uid) | |
} | |
} | |
override fun onUserJoined(uid: Int, elapsed: Int) { | |
super.onUserJoined(uid, elapsed) | |
Log.d("rohit", "user joind: $uid") | |
requireActivity().runOnUiThread { | |
setupRemoteVideo(uid) | |
} | |
} | |
} | |
private lateinit var mRtcEngine: RtcEngine | |
@SuppressLint("ClickableViewAccessibility") | |
override fun onCreateView( | |
inflater: LayoutInflater, | |
container: ViewGroup?, | |
savedInstanceState: Bundle? | |
): View { | |
binding = FragmentOngoingCallBinding.inflate(inflater, container, false) | |
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) | |
/*requireActivity().window.setFlags( | |
WindowManager.LayoutParams.FLAG_SECURE, | |
WindowManager.LayoutParams.FLAG_SECURE | |
)*/ | |
binding.flLocalContainer.setOnTouchListener( | |
View.OnTouchListener { view, event -> | |
when (event?.action) { | |
MotionEvent.ACTION_DOWN -> { | |
dX = view.x - event.rawX | |
dY = view.y - event.rawY | |
} | |
MotionEvent.ACTION_MOVE -> { | |
val displacement = event.rawX + dX | |
view.animate() | |
.x(displacement) | |
.y(event.rawY + dY) | |
.setDuration(0) | |
.start() | |
} | |
else -> { // Note the block | |
return@OnTouchListener false | |
} | |
} | |
true | |
} | |
) | |
return binding.root | |
} | |
@ExperimentalCoroutinesApi | |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | |
super.onViewCreated(view, savedInstanceState) | |
viewModel.loadSticker() | |
viewModel.getUserDetails() | |
viewModel.getHostDetails(navArgs.hostId) | |
initEngineAndJoinChannel() | |
collectUerProfile() | |
collectHostProfile() | |
initClick() | |
collectSticker() | |
stickerBinding.tvStickers.apply { | |
adapter = stickerAdapter | |
layoutManager = FlexboxLayoutManager(requireContext(), FlexDirection.ROW) | |
} | |
} | |
private fun collectHostProfile() = viewLifecycleOwner.lifecycleScope.launchWhenCreated { | |
viewModel.hostProfile.collectLatest { | |
when (it) { | |
is NetworkResponse.Failure -> { | |
findNavController().navigateUp() | |
} | |
is NetworkResponse.Loading -> { | |
} | |
is NetworkResponse.Success -> { | |
it.data?.let { hostProfile -> | |
hostProfileModel = hostProfile | |
} | |
} | |
} | |
} | |
} | |
private fun collectUerProfile() = | |
viewLifecycleOwner.lifecycleScope.launchWhenStarted { | |
viewModel.userDetails.collectLatest { | |
when (it) { | |
is NetworkResponse.Failure -> findNavController().navigateUp() | |
is NetworkResponse.Loading -> { | |
} | |
is NetworkResponse.Success -> { | |
it.data?.let { | |
userProfileModel = it | |
} | |
} | |
} | |
} | |
} | |
private fun collectSticker() { | |
viewLifecycleOwner.lifecycleScope.launchWhenStarted { | |
viewModel.stickerPack.collectLatest { networkResponse -> | |
when (networkResponse) { | |
is NetworkResponse.Failure -> { | |
} | |
is NetworkResponse.Loading -> { | |
} | |
is NetworkResponse.Success -> { | |
networkResponse.data?.let { | |
stickerAdapter.submitList(it) | |
} | |
} | |
} | |
} | |
} | |
} | |
private fun initClick() = binding.apply { | |
imgStickerPack.setOnClickListener { | |
stickerDialog.setContentView(stickerBinding.root) | |
stickerDialog.create() | |
stickerDialog.show() | |
collectMessages() | |
} | |
imgMute.setOnClickListener { | |
toggleMute(imgMute.isChecked) | |
} | |
imgflipCamera.setOnClickListener { | |
mRtcEngine.switchCamera() | |
} | |
imgEndCall.setOnClickListener { | |
endCall() | |
} | |
imgSpeaker.setOnClickListener { | |
toggetSwitch(imgSpeaker.isChecked) | |
} | |
imgSend.setOnClickListener { | |
if (etChatBox.text.toString().isNotEmpty()) { | |
viewModel.sendMessage( | |
messageModel = MessageModel(message = etChatBox.text.toString()), | |
navArgs.channelId | |
) | |
} else { | |
return@setOnClickListener | |
} | |
} | |
imgStickerPack.setOnClickListener { | |
stickerDialog.setContentView(stickerBinding.root) | |
stickerDialog.create() | |
stickerDialog.show() | |
} | |
} | |
private fun toggleMute(isMute: Boolean) { | |
mRtcEngine.muteLocalVideoStream(isMute) | |
} | |
private fun toggetSwitch(speakertoggle: Boolean) { | |
mRtcEngine.setEnableSpeakerphone(speakertoggle) | |
} | |
private fun endCall() { | |
} | |
private fun collectMessages() = viewLifecycleOwner.lifecycleScope.launchWhenStarted { | |
viewModel.messages.collectLatest { | |
when (it) { | |
is NetworkResponse.Failure -> { | |
} | |
is NetworkResponse.Loading -> { | |
} | |
is NetworkResponse.Success -> { | |
binding.rvChat.apply { | |
adapter = mAdapter | |
layoutManager = LinearLayoutManager(requireContext()) | |
} | |
it.data?.let { messageList -> | |
mAdapter.submitList(messageList) | |
binding.rvChat.smoothScrollToPosition(mAdapter.itemCount) | |
} | |
} | |
} | |
} | |
} | |
@AfterPermissionGranted(PERMISSION_REQ_ID_CAMERA) | |
private fun initEngineAndJoinChannel() { | |
if (EasyPermissions.hasPermissions( | |
requireContext(), | |
Manifest.permission.CAMERA, | |
Manifest.permission.RECORD_AUDIO | |
) | |
) { | |
initlizesEngine() | |
joinChannel() | |
setupLocalVideo() | |
} else { | |
EasyPermissions.requestPermissions( | |
this, | |
getString(R.string.permissions_needed), | |
PERMISSION_REQ_ID_RECORD_AUDIO, | |
Manifest.permission.CAMERA, | |
Manifest.permission.RECORD_AUDIO | |
) | |
} | |
} | |
private fun initlizesEngine() { | |
try { | |
mRtcEngine = RtcEngine.create( | |
requireContext(), | |
resources.getString(R.string.agora_app_id), | |
mRtcEngineEventHandler | |
) | |
setUpSession() | |
} catch (e: Exception) { | |
Log.d("test", "error: ${e.localizedMessage}") | |
} | |
} | |
private fun setUpSession() { | |
mRtcEngine.setChannelProfile(CHANNEL_PROFILE_COMMUNICATION) | |
mRtcEngine.enableVideo() | |
mRtcEngine.enableAudio() | |
mRtcEngine.setVideoEncoderConfiguration( | |
VideoEncoderConfiguration( | |
VD_640x480, | |
VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_30, | |
VideoEncoderConfiguration.STANDARD_BITRATE, | |
VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT | |
) | |
) | |
} | |
private fun setupLocalVideo() { | |
mRtcEngine.enableVideo() | |
mRtcEngine.enableAudio() | |
Log.d("test", "setup local") | |
val surface = RtcEngine.CreateRendererView(requireActivity().baseContext) | |
surface.setZOrderMediaOverlay(true) | |
binding.flLocalContainer.addView(surface) | |
mRtcEngine.setupLocalVideo(VideoCanvas(surface, VideoCanvas.RENDER_MODE_FIT, 0)) | |
mRtcEngine.enableVideo() | |
} | |
private fun joinChannel() { | |
mRtcEngine.joinChannel( | |
"006f3e0c67ee8d9449b97a77e44d7a934f8IADLhkMRMvaK2iz44nSwP/UFdl4tzeSwXXqU6W9p9r5sCgx+f9gAAAAAEADEGoyZS0l0YQEAAQBKSXRh", | |
"test", | |
"", | |
0 | |
) | |
// mRtcEngine.joinChannel(navArgs.userToken, navArgs.channelId, "", 0) | |
} | |
private fun setupRemoteVideo(uid: Int) { | |
Log.d("rohit", "in remote video") | |
val remoteSurface = RtcEngine.CreateRendererView(requireActivity().baseContext) | |
binding.flRemoteContainer.addView(remoteSurface) | |
mRtcEngine.setupRemoteVideo(VideoCanvas(remoteSurface, VideoCanvas.RENDER_MODE_FILL, uid)) | |
} | |
private fun removeRemoteVideo() { | |
binding.flRemoteContainer.removeView(mLocalView) | |
removeLocalVideo() | |
leaveChannel() | |
} | |
private fun removeLocalVideo() { | |
} | |
private fun leaveChannel() { | |
mRtcEngine.leaveChannel() | |
// TODO: 9/7/21 Handle UI after Remove | |
} | |
override fun onRequestPermissionsResult( | |
requestCode: Int, | |
permissions: Array<out String>, | |
grantResults: IntArray | |
) { | |
super.onRequestPermissionsResult(requestCode, permissions, grantResults) | |
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) | |
} | |
override fun onDestroy() { | |
super.onDestroy() | |
leaveChannel() | |
RtcEngine.destroy() | |
} | |
companion object { | |
private const val PERMISSION_REQ_ID_RECORD_AUDIO = 22 | |
private const val PERMISSION_REQ_ID_CAMERA = PERMISSION_REQ_ID_RECORD_AUDIO + 1 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment