Как сделать удаленным участником Twilio Video Chat в студии android - PullRequest
0 голосов
/ 04 мая 2020

В моем приложении я использую видеочат Twilio. Соединение установлено, но дело в том, что другой участник добавляет, что не может уведомить предыдущего пользователя, который уже подключен.

все сделано, но главная проблема - соединение между удаленным пользователем, с которым они не могут соединиться. если я использую два идентичных пользователя, отключите предыдущего пользователя и подключите нового к серверу. Пожалуйста, помогите мне. Это мой код


class VideoChatActivity : AppCompatActivity(), Room.Listener, KodeinAware, ApiListener {


    private val audioManager by lazy {
        this@VideoChatActivity.getSystemService(Context.AUDIO_SERVICE) as AudioManager
    }
    private val cameraCapturerCompat by lazy {
        CameraCapturerCompat(this, getAvailableCameraSource())
    }

    private var previousAudioMode = 0
    private var room: Room? = null
    private var previousMicrophoneMute = false
    private lateinit var localVideoView: VideoRenderer

    val VIDEO_CODEC_NAMES = arrayOf(Vp8Codec.NAME, H264Codec.NAME, Vp9Codec.NAME)

    val AUDIO_CODEC_NAMES = arrayOf(
        IsacCodec.NAME, OpusCodec.NAME, PcmaCodec.NAME,
        PcmuCodec.NAME, G722Codec.NAME
    )

    private val TAG = "MAin"

    private var participantIdentity: String? = null
    private lateinit var videoStatusTextView: TextView

    private var localParticipant: LocalParticipant? = null

    private val audioCodec: AudioCodec
        get() {
            val audioCodecName = OpusCodec.NAME
            return when (audioCodecName) {
                IsacCodec.NAME -> IsacCodec()
                OpusCodec.NAME -> OpusCodec()
                PcmaCodec.NAME -> PcmaCodec()
                PcmuCodec.NAME -> PcmuCodec()
                G722Codec.NAME -> G722Codec()
                else -> OpusCodec()
            }
        }

    private val videoCodec: VideoCodec
        get() {
            val videoCodecName = Vp8Codec.NAME

            return when (videoCodecName) {
                Vp8Codec.NAME -> {
                    val simulcast = false
                    Vp8Codec(simulcast)
                }
                H264Codec.NAME -> H264Codec()
                Vp9Codec.NAME -> Vp9Codec()
                else -> Vp8Codec()
            }
        }

    private var localAudioTrack: LocalAudioTrack? = null
    private var localVideoTrack: LocalVideoTrack? = null
    private lateinit var videoView: VideoView
    private lateinit var myVideoView: VideoView


    //api
    override val kodein by kodein()
    private val factory: ChatViewModelFactory by instance()
    private lateinit var viewModel: ChatViewModel
    private var loadingDialog: LoadingDialog? = null


    private var roomName = ""
    private var roomToken = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_video_chat)

        viewModel = ViewModelProviders.of(this, factory).get(ChatViewModel::class.java)
        viewModel.listener = this
        loadingDialog = LoadingDialog(this)


        videoView = findViewById(R.id.videoView)
        videoStatusTextView = findViewById(R.id.videoStatusTextView)
        myVideoView = findViewById(R.id.myVideoView)
        myVideoView.mirror = true


        viewModel.getVideoToken()


    }

    @SuppressLint("NewApi")
    private fun CheckPermissions() {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.CAMERA
            ) != PackageManager.PERMISSION_GRANTED ||
            ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.RECORD_AUDIO
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            requestPermissions(
                arrayOf(
                    Manifest.permission.CAMERA,
                    Manifest.permission.RECORD_AUDIO
                ),
                1001
            )
            return
        } else {
            OpenImageData()
        }
    }

    private fun OpenImageData() {

        audioManager.isSpeakerphoneOn = true
        createAudioAndVideoTracks()
        configureAudio(true)
        val connectOptionsBuilder =
            ConnectOptions.Builder(roomToken)
                .roomName("DailyStandup")
                .enableAutomaticSubscription(false)
        localAudioTrack?.let { connectOptionsBuilder.audioTracks(listOf(it)) }
        localVideoTrack?.let { connectOptionsBuilder.videoTracks(listOf(it)) }
        localVideoTrack?.addRenderer(myVideoView)
        connectOptionsBuilder.preferAudioCodecs(listOf(audioCodec))
        connectOptionsBuilder.preferVideoCodecs(listOf(videoCodec))
        connectOptionsBuilder.encodingParameters(EncodingParameters(0, 0))
        room = Video.connect(this, connectOptionsBuilder.build(), this)

    }

    private fun configureAudio(enable: Boolean) {
        with(audioManager) {
            if (enable) {
                previousAudioMode = audioManager.mode
                // Request audio focus before making any device switch
                requestAudioFocus()
                /*
                 * Use MODE_IN_COMMUNICATION as the default audio mode. It is required
                 * to be in this mode when playout and/or recording starts for the best
                 * possible VoIP performance. Some devices have difficulties with
                 * speaker mode if this is not set.
                 */
                mode = AudioManager.MODE_IN_COMMUNICATION
                /*
                 * Always disable microphone mute during a WebRTC call.
                 */
                previousMicrophoneMute = isMicrophoneMute
                isMicrophoneMute = false
            } else {
                mode = previousAudioMode
                abandonAudioFocus(null)
                isMicrophoneMute = previousMicrophoneMute
            }
        }
    }

    private fun requestAudioFocus() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val playbackAttributes = AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
                .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                .build()
            val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
                .setAudioAttributes(playbackAttributes)
                .setAcceptsDelayedFocusGain(true)
                .setOnAudioFocusChangeListener { }
                .build()
            audioManager.requestAudioFocus(focusRequest)
        } else {
            audioManager.requestAudioFocus(
                null, AudioManager.STREAM_VOICE_CALL,
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
            )
        }
    }


    @SuppressLint("NewApi")
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        when (requestCode) {
            1001 -> {
                var read = grantResults[0] == PackageManager.PERMISSION_GRANTED
                var write = grantResults[1] == PackageManager.PERMISSION_GRANTED
                if (grantResults.isNotEmpty() && read && write) {
                    OpenImageData()
                } else if (Build.VERSION.SDK_INT >= 23 && !shouldShowRequestPermissionRationale(
                        Manifest.permission.CAMERA
                    ) && !shouldShowRequestPermissionRationale(Manifest.permission.RECORD_AUDIO)
                ) {
                    rationale()
                } else {
                    requestPermissions(
                        arrayOf(
                            Manifest.permission.CAMERA,
                            Manifest.permission.RECORD_AUDIO
                        ),
                        1001
                    )
                }
            }
        }
    }

    private fun rationale() {
        val builder: AlertDialog.Builder =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                AlertDialog.Builder(
                    this,
                    android.R.style.Theme_Material_Light_Dialog_Alert
                )
            } else {
                AlertDialog.Builder(this)
            }
        builder.setTitle("Mandatory Permissions")
            .setMessage("Manually allow permissions in App settings")
            .setPositiveButton("Proceed") { dialog, which ->
                val intent = Intent()
                intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
                val uri = Uri.fromParts("package", packageName, null)
                intent.data = uri
                startActivityForResult(intent, 1)
            }
            .setCancelable(false)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .show()
    }


    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == 1) {
                val read: Int = ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.CAMERA
                )
                val write: Int = ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.RECORD_AUDIO
                )
                if (read != PackageManager.PERMISSION_GRANTED || write != PackageManager.PERMISSION_GRANTED) {
                    rationale()
                } else {
                    CheckPermissions()
                }
            }
        }
    }

    private fun createAudioAndVideoTracks() {
        // Share your microphone
        localAudioTrack = LocalAudioTrack.create(this, true)

        // Share your camera
        localVideoTrack = LocalVideoTrack.create(
            this,
            true,
            cameraCapturerCompat.videoCapturer
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        localVideoTrack?.release()
        localAudioTrack?.release()
    }


    private fun getAvailableCameraSource(): CameraCapturer.CameraSource {
        return if (CameraCapturer.isSourceAvailable(CameraCapturer.CameraSource.FRONT_CAMERA))
            CameraCapturer.CameraSource.FRONT_CAMERA
        else
            CameraCapturer.CameraSource.BACK_CAMERA
    }

    override fun onRecordingStopped(room: Room) {
        Log.e("VIDEO", "onRecordingStopped")
        videoStatusTextView.text = "Recording Stop"

    }

    override fun onParticipantDisconnected(room: Room, remoteParticipant: RemoteParticipant) {
        Log.e("VIDEO", "onParticipantDisconnected")
        videoStatusTextView.text = "ParticipantDisconnected"

    }

    override fun onRecordingStarted(room: Room) {
        Log.e("VIDEO", "onRecordingStarted")
        videoStatusTextView.text = "onRecordingStarted"

    }

    override fun onConnectFailure(room: Room, twilioException: TwilioException) {
        Log.e("VIDEO", "onConnectFailure" + twilioException.message)
        videoStatusTextView.text = "onConnectFailure"

    }

    override fun onReconnected(room: Room) {
        Log.e("VIDEO", "onReconnected")
        videoStatusTextView.text = "onReconnected"

    }

    override fun onParticipantConnected(room: Room, remoteParticipant: RemoteParticipant) {
        Log.e("VIDEO", "Particepate Connected")
        videoStatusTextView.text = "onParticipantConnected"
        addRemoteParticipant(remoteParticipant)

    }

    override fun onResume() {
        super.onResume()
        /*
         * If the local video track was released when the app was put in the background, recreate.
         */
        localVideoTrack = if (localVideoTrack == null) {
            LocalVideoTrack.create(
                this,
                true,
                cameraCapturerCompat.videoCapturer
            )
        } else {
            localVideoTrack
        }
        localVideoTrack?.addRenderer(myVideoView)

        audioManager.isSpeakerphoneOn = true
        /*
         * If connected to a Room then share the local video track.
         */
        localVideoTrack?.let { localParticipant?.publishTrack(it) }

        /*
         * Update encoding parameters if they have changed.
         */
        localParticipant?.setEncodingParameters(EncodingParameters(0, 0))

        /*
         * Route audio through cached value.
         */
        audioManager.isSpeakerphoneOn = true

        /*
         * Update reconnecting UI
         */
        room?.let {
//            reconnectingProgressBar.visibility = if (it.state != Room.State.RECONNECTING)
//                View.GONE else
//                View.VISIBLE
            videoStatusTextView.text = "Connected to ${it.name}"
        }
    }


    override fun onConnected(room: Room) {
        localParticipant = room.localParticipant
        Log.e("VIDEO", "Connected")
        Log.e("VIDEO", room.localParticipant.toString())
        videoStatusTextView.text = "onConnected"
        room.remoteParticipants.firstOrNull()?.let { addRemoteParticipant(it) }
    }

    override fun onDisconnected(room: Room, twilioException: TwilioException?) {
        videoStatusTextView.text = "onDisconnected"
        if (twilioException != null) {
            Log.e("VIDEO", "Disonnected" + twilioException.message)
            room.disconnect()
        }
    }

    override fun onReconnecting(room: Room, twilioException: TwilioException) {
        videoStatusTextView.text = "onReconnecting"
        Log.e("VIDEO", "Reconnected" + twilioException.message)
    }


    private fun addRemoteParticipant(remoteParticipant: RemoteParticipant) {
        Log.e("VIDEO", "ADD")

        /*
         * This app only displays video for one additional participant per Room
         */
//        if (thumbnailVideoView.visibility == View.VISIBLE) {
//            // Snackbar.make(connectActionFab,
//            //   "Multiple participants are not currently support in this UI",
//            //     Snackbar.LENGTH_LONG)
//            //       .setAction("Action", null).show()
//            return
//        }

        participantIdentity = remoteParticipant.identity

        //videoStatusTextView.text = "Participant $participantIdentity joined"

        /*
         * Add participant renderer
         */


        remoteParticipant.remoteVideoTracks.firstOrNull()?.let { remoteVideoTrackPublication ->
            if (remoteVideoTrackPublication.isTrackSubscribed) {
                remoteVideoTrackPublication.remoteVideoTrack?.let { addRemoteParticipantVideo(it) }
            }
        }

        /*
         * Start listening for participant events
         */
        remoteParticipant.setListener(participantListener)
    }


    private fun addRemoteParticipantVideo(videoTrack: VideoTrack) {
//        moveLocalVideoToThumbnailView()
        primaryVideoView.mirror = false
        videoTrack.addRenderer(primaryVideoView)
    }


    private val participantListener = object : RemoteParticipant.Listener {
        override fun onAudioTrackPublished(
            remoteParticipant: RemoteParticipant,
            remoteAudioTrackPublication: RemoteAudioTrackPublication
        ) {
            Log.i(
                TAG, "onAudioTrackPublished: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteAudioTrackPublication: sid=${remoteAudioTrackPublication.trackSid}, " +
                        "enabled=${remoteAudioTrackPublication.isTrackEnabled}, " +
                        "subscribed=${remoteAudioTrackPublication.isTrackSubscribed}, " +
                        "name=${remoteAudioTrackPublication.trackName}]"
            )
            videoStatusTextView.text = "onAudioTrackAdded"
        }

        override fun onAudioTrackUnpublished(
            remoteParticipant: RemoteParticipant,
            remoteAudioTrackPublication: RemoteAudioTrackPublication
        ) {
            Log.i(
                TAG, "onAudioTrackUnpublished: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteAudioTrackPublication: sid=${remoteAudioTrackPublication.trackSid}, " +
                        "enabled=${remoteAudioTrackPublication.isTrackEnabled}, " +
                        "subscribed=${remoteAudioTrackPublication.isTrackSubscribed}, " +
                        "name=${remoteAudioTrackPublication.trackName}]"
            )
            videoStatusTextView.text = "onAudioTrackRemoved"
        }

        override fun onDataTrackPublished(
            remoteParticipant: RemoteParticipant,
            remoteDataTrackPublication: RemoteDataTrackPublication
        ) {
            Log.i(
                TAG, "onDataTrackPublished: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteDataTrackPublication: sid=${remoteDataTrackPublication.trackSid}, " +
                        "enabled=${remoteDataTrackPublication.isTrackEnabled}, " +
                        "subscribed=${remoteDataTrackPublication.isTrackSubscribed}, " +
                        "name=${remoteDataTrackPublication.trackName}]"
            )
            videoStatusTextView.text = "onDataTrackPublished"
        }

        override fun onDataTrackUnpublished(
            remoteParticipant: RemoteParticipant,
            remoteDataTrackPublication: RemoteDataTrackPublication
        ) {
            Log.i(
                TAG, "onDataTrackUnpublished: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteDataTrackPublication: sid=${remoteDataTrackPublication.trackSid}, " +
                        "enabled=${remoteDataTrackPublication.isTrackEnabled}, " +
                        "subscribed=${remoteDataTrackPublication.isTrackSubscribed}, " +
                        "name=${remoteDataTrackPublication.trackName}]"
            )
            videoStatusTextView.text = "onDataTrackUnpublished"
        }

        override fun onVideoTrackPublished(
            remoteParticipant: RemoteParticipant,
            remoteVideoTrackPublication: RemoteVideoTrackPublication
        ) {
            Log.i(
                TAG, "onVideoTrackPublished: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteVideoTrackPublication: sid=${remoteVideoTrackPublication.trackSid}, " +
                        "enabled=${remoteVideoTrackPublication.isTrackEnabled}, " +
                        "subscribed=${remoteVideoTrackPublication.isTrackSubscribed}, " +
                        "name=${remoteVideoTrackPublication.trackName}]"
            )
            videoStatusTextView.text = "onVideoTrackPublished"
        }

        override fun onVideoTrackUnpublished(
            remoteParticipant: RemoteParticipant,
            remoteVideoTrackPublication: RemoteVideoTrackPublication
        ) {
            Log.i(
                TAG, "onVideoTrackUnpublished: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteVideoTrackPublication: sid=${remoteVideoTrackPublication.trackSid}, " +
                        "enabled=${remoteVideoTrackPublication.isTrackEnabled}, " +
                        "subscribed=${remoteVideoTrackPublication.isTrackSubscribed}, " +
                        "name=${remoteVideoTrackPublication.trackName}]"
            )
            videoStatusTextView.text = "onVideoTrackUnpublished"
        }

        override fun onAudioTrackSubscribed(
            remoteParticipant: RemoteParticipant,
            remoteAudioTrackPublication: RemoteAudioTrackPublication,
            remoteAudioTrack: RemoteAudioTrack
        ) {
            Log.i(
                TAG, "onAudioTrackSubscribed: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteAudioTrack: enabled=${remoteAudioTrack.isEnabled}, " +
                        "playbackEnabled=${remoteAudioTrack.isPlaybackEnabled}, " +
                        "name=${remoteAudioTrack.name}]"
            )
            videoStatusTextView.text = "onAudioTrackSubscribed"
        }

        override fun onAudioTrackUnsubscribed(
            remoteParticipant: RemoteParticipant,
            remoteAudioTrackPublication: RemoteAudioTrackPublication,
            remoteAudioTrack: RemoteAudioTrack
        ) {
            Log.i(
                TAG, "onAudioTrackUnsubscribed: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteAudioTrack: enabled=${remoteAudioTrack.isEnabled}, " +
                        "playbackEnabled=${remoteAudioTrack.isPlaybackEnabled}, " +
                        "name=${remoteAudioTrack.name}]"
            )
            videoStatusTextView.text = "onAudioTrackUnsubscribed"
        }

        override fun onAudioTrackSubscriptionFailed(
            remoteParticipant: RemoteParticipant,
            remoteAudioTrackPublication: RemoteAudioTrackPublication,
            twilioException: TwilioException
        ) {
            Log.i(
                TAG, "onAudioTrackSubscriptionFailed: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteAudioTrackPublication: sid=${remoteAudioTrackPublication.trackSid}, " +
                        "name=${remoteAudioTrackPublication.trackName}]" +
                        "[TwilioException: code=${twilioException.code}, " +
                        "message=${twilioException.message}]"
            )
            videoStatusTextView.text = "onAudioTrackSubscriptionFailed"
        }

        override fun onDataTrackSubscribed(
            remoteParticipant: RemoteParticipant,
            remoteDataTrackPublication: RemoteDataTrackPublication,
            remoteDataTrack: RemoteDataTrack
        ) {
            Log.i(
                TAG, "onDataTrackSubscribed: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteDataTrack: enabled=${remoteDataTrack.isEnabled}, " +
                        "name=${remoteDataTrack.name}]"
            )
            videoStatusTextView.text = "onDataTrackSubscribed"
        }

        override fun onDataTrackUnsubscribed(
            remoteParticipant: RemoteParticipant,
            remoteDataTrackPublication: RemoteDataTrackPublication,
            remoteDataTrack: RemoteDataTrack
        ) {
            Log.i(
                TAG, "onDataTrackUnsubscribed: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteDataTrack: enabled=${remoteDataTrack.isEnabled}, " +
                        "name=${remoteDataTrack.name}]"
            )
            videoStatusTextView.text = "onDataTrackUnsubscribed"
        }

        override fun onDataTrackSubscriptionFailed(
            remoteParticipant: RemoteParticipant,
            remoteDataTrackPublication: RemoteDataTrackPublication,
            twilioException: TwilioException
        ) {
            Log.i(
                TAG, "onDataTrackSubscriptionFailed: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteDataTrackPublication: sid=${remoteDataTrackPublication.trackSid}, " +
                        "name=${remoteDataTrackPublication.trackName}]" +
                        "[TwilioException: code=${twilioException.code}, " +
                        "message=${twilioException.message}]"
            )
            videoStatusTextView.text = "onDataTrackSubscriptionFailed"
        }

        override fun onVideoTrackSubscribed(
            remoteParticipant: RemoteParticipant,
            remoteVideoTrackPublication: RemoteVideoTrackPublication,
            remoteVideoTrack: RemoteVideoTrack
        ) {
            Log.i(
                TAG, "onVideoTrackSubscribed: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteVideoTrack: enabled=${remoteVideoTrack.isEnabled}, " +
                        "name=${remoteVideoTrack.name}]"
            )
            videoStatusTextView.text = "onVideoTrackSubscribed"
            addRemoteParticipantVideo(remoteVideoTrack)
        }

        override fun onVideoTrackUnsubscribed(
            remoteParticipant: RemoteParticipant,
            remoteVideoTrackPublication: RemoteVideoTrackPublication,
            remoteVideoTrack: RemoteVideoTrack
        ) {
            Log.i(
                TAG, "onVideoTrackUnsubscribed: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteVideoTrack: enabled=${remoteVideoTrack.isEnabled}, " +
                        "name=${remoteVideoTrack.name}]"
            )
            videoStatusTextView.text = "onVideoTrackUnsubscribed"
            removeParticipantVideo(remoteVideoTrack)
        }

        override fun onVideoTrackSubscriptionFailed(
            remoteParticipant: RemoteParticipant,
            remoteVideoTrackPublication: RemoteVideoTrackPublication,
            twilioException: TwilioException
        ) {
            Log.i(
                TAG, "onVideoTrackSubscriptionFailed: " +
                        "[RemoteParticipant: identity=${remoteParticipant.identity}], " +
                        "[RemoteVideoTrackPublication: sid=${remoteVideoTrackPublication.trackSid}, " +
                        "name=${remoteVideoTrackPublication.trackName}]" +
                        "[TwilioException: code=${twilioException.code}, " +
                        "message=${twilioException.message}]"
            )
            videoStatusTextView.text = "onVideoTrackSubscriptionFailed"
        }

        override fun onAudioTrackEnabled(
            remoteParticipant: RemoteParticipant,
            remoteAudioTrackPublication: RemoteAudioTrackPublication
        ) {
        }

        override fun onVideoTrackEnabled(
            remoteParticipant: RemoteParticipant,
            remoteVideoTrackPublication: RemoteVideoTrackPublication
        ) {
        }

        override fun onVideoTrackDisabled(
            remoteParticipant: RemoteParticipant,
            remoteVideoTrackPublication: RemoteVideoTrackPublication
        ) {
        }

        override fun onAudioTrackDisabled(
            remoteParticipant: RemoteParticipant,
            remoteAudioTrackPublication: RemoteAudioTrackPublication
        ) {
        }
    }

    private fun removeParticipantVideo(videoTrack: VideoTrack) {
        videoTrack.removeRenderer(videoView)
    }

    override fun onStarted() {
        loadingDialog?.setLoading(true)
    }

    override fun <T> onSuccess(response: T) {
        loadingDialog?.setLoading(false)

        var data = response as ApiResponse<VideoTokenResult>
        roomName = data.result?.Room.toString()
        roomToken = data.result?.Token.toString()
        CheckPermissions()
    }

    override fun onFailer(message: String) {
        loadingDialog?.setLoading(false)
    }

    override fun onUnauthorize(message: String, code: Int) {
        loadingDialog?.setLoading(false)
    }
}



Нельзя перейти в AddRemoteParticipant

...