Calling a phone number while app is in background - android

I'm trying to develop an app that:
Listens to push notifications
If the push notification is from WhatsApp + contains certain info, the app should call a specific number.
For the sake of the argument, let's assume that both permissions (call + notification listener) have already been granted.
So I used the below code (and of course, added the listener to the manifest), which works while the app is in the front, but not when it's in the background or closed. I also tried replacing "startActivity" with "startService", but that didn't work either. What's the correct way to leave the service running in the background and actually calling a number even though the app is in the background or closed? Also, is there a certain way to achieve this even the phone is locked?
class NotificationListener : NotificationListenerService() {
companion object {
private const val TAG = "NotificationListener"
private const val WA_PACKAGE = "com.whatsapp"
}
override fun onListenerConnected() {
Log.i(TAG, "Notification Listener connected")
Toast.makeText(applicationContext, "Notification Listener connected", Toast.LENGTH_SHORT).show()
}
override fun onNotificationPosted(sbn: StatusBarNotification) {
if (sbn.packageName != WA_PACKAGE) {
return
}
val notification = sbn.notification
val extras: Bundle = notification.extras
val from = extras.getString(NotificationCompat.EXTRA_TITLE)
val message = extras.getString(NotificationCompat.EXTRA_TEXT)
if (from != null && from.contains("test") && message != null && message.contains("gate")) {
val msg = "[$from]\n[$message]"
Log.i(TAG, msg)
Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
attemptCallGate()
}
}
private fun attemptCallGate() {
when (ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
true -> callGate()
false -> Toast.makeText(applicationContext, R.string.access_denied, Toast.LENGTH_SHORT).show()
}
}
private fun callGate() {
val number = "1234567890"
try {
val callIntent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$number"))
callIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
Toast.makeText(applicationContext, "Attempting to call [$number]", Toast.LENGTH_SHORT).show()
startActivity(callIntent)
} catch (e: Exception) {
Toast.makeText(applicationContext, "Failed calling [$number] ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}

Related

why my failure toast appears continuously in androi kotlin

I don't know how to make my failure toast message to show only once.
Toast.makeText(this, vm.logInResult.value, Toast.LENGTH_SHORT).show()
private fun addData(edtTxt: String, pasTxt: String) {
val repository = UserRepository()
val viewModelFactory = UserViewModelFactory(repository)
viewModel = ViewModelProvider(this, viewModelFactory).get(UserViewModel::class.java)
viewModel.pushUser(edtTxt, pasTxt)
viewModel.userPush.observe(this, Observer { response ->
if (response.isSuccessful) {
dismissLogoProgressDialog()
Log.d("MainResponse", response.body().toString())
Log.d("MainExecute", response.code().toString())
Log.d("Main", response.message())
val check = response.body()
Log.d("checkdata", "" + check?.userinfo?.email)
val tokn: String = check!!.token
if (sharedPreference.getValueString("token") != null) {
sharedPreference.clearSharedPreference()
}
sharedPreference.save("token", tokn)
sharedPreference.save("login_uid", check.userinfo.uid)
sharedPreference.save("change_pass", pasTxt)
println(check)
startActivity(Intent(this, DashboardActivity::class.java))
finish()
} else {
dismissLogoProgressDialog()
Toast.makeText(this, "Password mismatch", Toast.LENGTH_SHORT).show()
}
})
}
Are you sure you only call this Toast once? Or is this Toast created in a loop? In that case; you need to breakout of the loop first.
The function may have been placed within a loop and the else clause is may always be taken.
Are the Log functions printing anything to the console?
Is there anyway you could edit the question and show us where this function is called?

WifiP2PManager.WIFI_P2P_PEERS_CHANGED_ACTION is broadcasting instead of WIFI_P2P_CONNECTION_CHANGED_ACTION when connected to a peer

I am trying to connect two devices using WifiP2Pmanager in android.
Able to list the peers and connect the selected device.
Below given code snippet executes when a peer is selected to connect
val device = deviceArray[position]
val config = WifiP2pConfig()
config.deviceAddress = device.deviceAddress
config.wps.setup = WpsInfo.PBC
channel?.also { channel ->
manager?.connect(channel, config, object : WifiP2pManager.ActionListener {
override fun onSuccess() {
Toast.makeText(applicationContext, "Connected to " + device.deviceName, Toast.LENGTH_SHORT).show()
}
override fun onFailure(reason: Int) {
Toast.makeText(applicationContext, "Not Connected", Toast.LENGTH_SHORT).show()
}
}
)}
But after the peer connection, the intent broadcasting is WIFI_P2P_PEERS_CHANGED_ACTION instead of WIFI_P2P_CONNECTION_CHANGED_ACTION. Trying on the device with android version 7.
I followed all the steps and code as explained in android WifiDirect .
This is my onReceive code part in BroadcastReceiver implementation.
override fun onReceive(context: Context, intent: Intent) {
val action: String? = intent.action
when (action) {
WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
// Check to see if Wi-Fi is enabled and notify appropriate activity
val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1)
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
Toast.makeText(context, "Wifi is ON", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Wifi is OFF", Toast.LENGTH_SHORT).show()
}
}
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
// Call WifiP2pManager.requestPeers() to get a list of current peers
if (manager != null) {
manager.requestPeers(channel, activity.peerListListener);
}
}
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
// Respond to new connection or disconnections
if (manager == null) {
return
}
if (isNetworkAvailable(context)) {
activity.connectionStatus.setText("Device Connected")
manager.requestConnectionInfo(channel, activity.connectionInfoListener)
} else {
activity.connectionStatus.setText("Device Disconnected")
}
}
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> {
// Respond to this device's wifi state changing
}
}
}
Any help would be much appreciated..

Detect NFC tag through all the activities

I'm working on Android NFC based application with requirement of continuously read/write data to SLIX-2(ICode) tag from any activity.
As of now, application starts to initialize NFCManager which does most of the heavy lifting for Tag detection, continuously polling for presence check, read & write data.
BaseActivity does initialization of ANFCManager with other required work such as pending restart Intent, check nfc adapter, enableForegroundDispatch, ...
private fun initField() {
mNfcManager = ANfcManager(this)
}
private fun createPendingRestartIntent() {
pendingIntent = PendingIntent.getActivity(this, 0, Intent(this, javaClass)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0
)
}
override fun onResume() {
super.onResume()
try {
if(mNfcManager.checkNfcPowerStatus()) // NfcAdapter enabled or not
setReadyToHandleTag()
else Log.w(TAG, "Nfc is not supported or disabled.")
} catch (e: AcmNfcManager.NfcNotEnabledException) {
Log.e(TAG, "Nfc not enabled", e)
}
}
private fun setReadyToHandleTag() {
try {
TECHLISTS = arrayOf(arrayOf(IsoDep::class.java.name), arrayOf(NfcV::class.java.name),
arrayOf(NfcA::class.java.name), arrayOf(NfcB::class.java.name),
arrayOf(NfcF::class.java.name),arrayOf(Ndef::class.java.name),
arrayOf(NdefFormatable::class.java.name))
val tagDetected = IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)
tagDetected.addCategory(Intent.CATEGORY_DEFAULT)
TAGFILTERS = arrayOf(tagDetected)
} catch (e: Exception) {
Log.e(TAG, "TECH or TAG filter no detected!!!" )
}
pendingIntent?.let { mNfcManager.enableForegroundDispatch(this, it, TAGFILTERS, TECHLISTS) }
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
nfcState = mNfcManager.filterIntent(intent)
dispatchActionOnTag(nfcState)
}
// this abs function will provide the Tag state in the corresponding class
abstract fun dispatchActionOnTag(tag: Boolean)
Each Activity has NfcListener for tag detection and will do the read/write using ANfcManager API's. Also to continuously checking the tag presence, using handler with looper internal class inside NFC Manager for presence check.
Here is the function inside ActivityA which trigger the method after tag detection as well as presence check thread,
override fun dispatchActionOnTag(tag: Boolean) {
mNfcStatus = tag
if (nfcStateListener() != null) {
nfcStateListener().updateNfcState(tag)
mNfcManager.startTagCheck() // presence check handler every x sec
}
}
This same function is been repeated(kind of not clean but still works) in each of the activity for tag detection and presence check & based on that read/write data to the Tag.
Here comes my problem,
Preconditions :
Tag in my application(product) is at a fixed location(sticked in a hardware) & is not usually taken out unless there is a tag change.
There are situations where Tag can be taken out in mostly ActivityB or ActivityC activity will be running, which required to repeat the same callback code in these activities.
Required:
- When switching from ActvityA-> ActivityB, Tag detection flow is not done(onNewIntent) or TAg is not taken out from proximity and tapped again. How will I write/read data to the tag?
ANFCManager,
class ANfcManager #Inject constructor(context: Context) {
private val mContext = context
private lateinit var nfcAdapter: NfcAdapter
private lateinit var mTag: Tag
private lateinit var iCodeTag: ICodeSlix2
private lateinit var icode: ICode
init {
val readPermission = ContextCompat.checkSelfPermission(
mContext,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
if (!readPermission) {
ActivityCompat.requestPermissions(
mContext as Activity,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 113
)
}
/**
* initialize background thread for presence check every x seconds.
*/
val thread = HandlerThread("PresenceCheckThread")
thread.start()
mHandler = PresenceHandler(thread.looper)
}
fun enableForegroundDispatch(
activity: FragmentActivity, intent: PendingIntent,
filters: Array<IntentFilter>?, techLists: Array<Array<String>>?
) {
nfcAdapter.enableForegroundDispatch(activity, intent, filters, techLists)
}
fun disableForegroundDispatch(activity: Activity) {
nfcAdapter.disableForegroundDispatch(activity)
}
fun filterIntent(intent: Intent): Boolean {
val action = intent.action
if (NfcAdapter.ACTION_TECH_DISCOVERED == action
|| NfcAdapter.ACTION_TAG_DISCOVERED == action
|| NfcAdapter.ACTION_NDEF_DISCOVERED == action
) {
if (intent.hasExtra(NfcAdapter.EXTRA_TAG)) {
mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)!!
return if (discoverTag()) {
Toast.makeText(mContext, "Tag detected.", Toast.LENGTH_SHORT).show()
true
} else {
ignoreTag()
false
}
}
}
return false
}
/**
* discover the Tag family.
*/
fun discoverTag(): Boolean {
icode = getTag(mTag)
if (ICodeSlix2::class.java.isInstance(icode))
iCodeTag = icode as ICodeSlix2
return iCodeTag != null
}
fun checkNfcPowerStatus(): Boolean {
return checkNfcPowerStatus(mContext)
}
/**
* Check Nfc status
*/
private fun checkNfcPowerStatus(context: Context?): Boolean {
nfcAdapter = NfcAdapter.getDefaultAdapter(context)
var enabled = false
if (nfcAdapter != null) {
enabled = nfcAdapter.isEnabled
}
return enabled
}
fun writeUpdateBlocks() {
try {
iCodeTag.connect()
.
. // proprietary code
.
}catch (e: IOException) {
e.printStackTrace()
Log.e(TAG, "IOException: ", e)
} catch (e: SmartCardException) {
e.printStackTrace()
Log.e(TAG, "SmartCardException: ", e)
} catch (e: IllegalArgumentException) {
e.printStackTrace()
Log.e(TAG, "IllegalArgumentException: ", e)
} catch (e: IllegalStateException) {
e.printStackTrace()
Log.e(TAG, "IllegalArgumentException: ", e)
} catch (e: IndexOutOfBoundsException) {
e.printStackTrace()
Log.e(TAG, "IndexOutOfBoundsException: ", e)
} finally {
iCodeTag.close()
}
}
Required: - When switching from ActvityA-> ActivityB, Tag detection
flow is not done(onNewIntent) or TAg is not taken out from proximity
and tapped again. How will I write/read data to the tag?
So the Tag object is a Parcelable Object , just pass it from ActivityA to ActivityB, you don't need to re-discover it.
e.g. something like (sorry in Java not Kotlin)
ActivityA
Intent intent = new Intent(getActivity(), ActivityB.class);
intent.putExtra("TAG", mTag);
startActivity(intent);
The in ActivityB onCreate
Intent intent = getIntent();
mTag = intent.getParcelableExtra("TAG")
// Start doing stuff with the Tag just like if you got it via discovery
// ANfcManager might need a `setTag` method to set it without discovery.
// or allow a Tag be be passed in the ANfcManager constructor
Not that I would use enableForegroundDispatch for reading and especially writing to Tags as I found it too unreliable, I would recommend enableReaderMode but then you can still pass the Tag Object between activities.
It was quick to convert the Manager class as Singleton and rest all remain same.
BaseActivity,
fun initField() {
mNfcManager = ANfcManager.getInstance(this)
}
class ANfcManager private constructor(context: Context){
companion object : SingletonHolder<ANfcManager, Context>(::ANfcManager) {
val TAG = ANfcManager::class.java.simpleName
}
init{
mContext = context
.
.
.
}
}

Unable to bind service (attempts always return false). Why?

I'm trying to wrap my head around the android bluetooth API by adapting this half-finished example project to get it working on a BLE heart rate peripheral (the stock HR example from Espressif, running on an ESP32 dev board).
My problem is that I am unable to bind the Service that manages the BLE connection; calling bindService always returns false (see commented line in initBLEService in code snippet below). I am unable to understand why, nor how to get the service running properly. Help?
Here's how I'm managing the BLE connection:
object BLEConnectionManager {
private val TAG = "BLEConnectionManager"
private var mBLEService: BLEService? = null
private var isBind = false
private val mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, service: IBinder) {
mBLEService = (service as BLEService.LocalBinder).getService()
Log.i(TAG, "BLEConnectionManager.onServiceConnected mBLEService = $mBLEService")
if (!mBLEService?.initialize()!!) {
Log.e(TAG, "Unable to initialize")
}
}
override fun onServiceDisconnected(componentName: ComponentName) {
mBLEService = null
}
}
fun initBLEService(context: Context) {
try {
if (mBLEService == null) {
val gattServiceIntent = Intent(context, BLEService::class.java)
if (context != null) {
// BELOW LINE ALWAYS RETURNS false. WHY?
isBind = context.bindService(gattServiceIntent, mServiceConnection,
Context.BIND_AUTO_CREATE)
Log.i(TAG, "BLEConnectionManager.initBLEService isBind = $isBind")
}
}
} catch (e: Exception) {
Log.e(TAG, e.message)
}
}
fun connect(deviceAddress: String): Boolean {
var result = false
Log.i(TAG, "BLEConnectionManager.connect (to $deviceAddress) and mBLEService is $mBLEService")
if (mBLEService != null) result = mBLEService!!.connect(deviceAddress)
return result
}
// ...etc
And here's what's going on in the main activity onCreate:
if (!BLEDeviceManager.isEnabled()) {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
}
BLEConnectionManager.initBLEService(this#MainActivity)
And I attempt to connect with a button in the main activity:
private fun connectDevice() {
Handler().postDelayed({
BLEConnectionManager.initBLEService(this#MainActivity)
if (BLEConnectionManager.connect(mDeviceAddress)) {
Toast.makeText(this#MainActivity, "DEVICE CONNECTED", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this#MainActivity, "DEVICE CONNECTION FAILED", Toast.LENGTH_SHORT).show()
}
}, 1000)
}
The BLEService class is unchanged from the original code.
Ugh. I didn't realize that the service must be declared in the manifest file to be available to the component for binding. Add:
<service android:name=".blemodule.BLEService"></service>

Enable Audio method issue in android agora rtc sdk

I am using interactive video broadcasting in my app.
I am attaching class in which I am using live streaming.
I am getting the audio issue when I go back from the live streaming screen to the previous screen. I still listen to the audio of the host.
previously I was using leave channel method and destroying rtc client object, but after implementing this when I go back from streaming class then it closes all users screen who are using this app because of leave channel method. after that, I removed this option from my on destroy method.
Now I am using disable audio method which disables the audio but when I open live streaming class it doesn't enable audio. Enable audio method is not working I also used the mute audio local stream method and rtc handler on user mute audio method.
I am getting error--
"LiveStreamingActivity has leaked IntentReceiver io.agora.rtc.internal.AudioRoutingController$HeadsetBroadcastReceiver#101a7a7
that was originally registered here. Are you missing a call to
unregisterReceiver()? android.app.IntentReceiverLeaked: Activity
com.allin.activities.home.homeActivities.LiveStreamingActivity has
leaked IntentReceiver
io.agora.rtc.internal.AudioRoutingController$HeadsetBroadcastReceiver#101a7a7
that was originally registered here. Are you missing a call to
unregisterReceiver()?"
Receiver is registering in SDK and exception is coming inside the SDK that is jar file I can't edit.
Please help this in resolving my issue as I have to live the app on
play store.
//firstly I have tried this but it automatically stops other
devices streaming.
override fun onDestroy() {
/* if (mRtcEngine != null) {
leaveChannel()
RtcEngine.destroy(mRtcEngine)
mRtcEngine = null
}*/
//second I have tried disabling the audio so that user will
not hear
the host voice
if (mRtcEngine != null) //
{
mRtcEngine!!.disableAudio()
}
super.onDestroy()
}
// then I when I came back from the previous screen to live streaming activity everything is initializing again but the audio is not able to audible.
override fun onResume() {
super.onResume()
Log.e("resume", "resume")
if (mRtcEngine != null) {
mRtcEngine!!.enableAudio()
// mRtcEngine!!.resumeAudio()
}
}
code I am using
//agora rtc engine and handler initialization-----------------
private var mRtcEngine: RtcEngine? = null
private var mRtcEventHandler = object : IRtcEngineEventHandler() {
#SuppressLint("LongLogTag")
override fun onFirstRemoteVideoDecoded(uid: Int, width: Int,
height: Int, elapsed: Int) {
}
override fun onUserOffline(uid: Int, reason: Int) {
runOnUiThread {
val a = reason //if login =0 user is offline
try {
if (mUid == uid) {
if (surfaceView?.parent != null)
(surfaceView?.parent as ViewGroup).removeAllViews()
if (mRtcEngine != null) {
leaveChannel()
RtcEngine.destroy(mRtcEngine)
mRtcEngine = null
}
setResult(IntentConstants.REQUEST_CODE_LIVE_STREAMING)
finish()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
override fun onUserMuteVideo(uid: Int, muted: Boolean) {
runOnUiThread {
// onRemoteUserVideoMuted(uid, muted);
Log.e("video","muted")
}
}
override fun onAudioQuality(uid: Int, quality: Int, delay:
Short, lost: Short) {
super.onAudioQuality(uid, quality, delay, lost)
Log.e("", "")
}
override fun onUserJoined(uid: Int, elapsed: Int) {
// super.onUserJoined(uid, elapsed)
mUid = uid
runOnUiThread {
try {
setupRemoteVideo(mUid!!)
} catch (e: Exception) {
e.printStackTrace()
}
}
Log.e("differnt_uid----", mUid.toString())
}
}
private fun initAgoraEngineAndJoinChannel() {
if(mRtcEngine==null)
{
initializeAgoraEngine()
setupVideoProfile()
}
}
//initializing rtc engine class
#Throws(Exception::class)
private fun initializeAgoraEngine() {
try {
var s = RtcEngine.getSdkVersion()
mRtcEngine = RtcEngine.create(baseContext, AgoraConstants.APPLICATION_ID, mRtcEventHandler)
} catch (e: Exception) {
// Log.e(LOG_TAG, Log.getStackTraceString(e));
throw RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e))
}
}
#Throws(Exception::class)
private fun setupVideoProfile() {
//mRtcEngine?.muteAllRemoteAudioStreams(true)
// mLogger.log("channelName account = " + channelName + ",uid = " + 0);
mRtcEngine?.enableVideo()
//mRtcEngine.clearVideoCompositingLayout();
mRtcEngine?.enableLocalVideo(false)
mRtcEngine?.setEnableSpeakerphone(false)
mRtcEngine?.muteLocalAudioStream(true)
joinChannel()
mRtcEngine?.setVideoProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING, true)
mRtcEngine?.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING)
mRtcEngine?.setClientRole(Constants.CLIENT_ROLE_AUDIENCE,"")
val speaker = mRtcEngine?.isSpeakerphoneEnabled
val camerafocus = mRtcEngine?.isCameraAutoFocusFaceModeSupported
Log.e("", "")
}
#Throws(Exception::class)
private fun setupRemoteVideo(uid: Int) {
val container = findViewById<FrameLayout>(R.id.fl_video_container)
if (container.childCount >= 1) {
return
}
surfaceView = RtcEngine.CreateRendererView(baseContext)
container.addView(surfaceView)
mRtcEngine?.setupRemoteVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_HIDDEN, uid))
mRtcEngine?.setRemoteVideoStreamType(uid, 1)
mRtcEngine?.setCameraAutoFocusFaceModeEnabled(false)
mRtcEngine?.muteRemoteAudioStream(uid, false)
mRtcEngine?.adjustPlaybackSignalVolume(0)
// mRtcEngine.setVideoProfile(Constants.VIDEO_PROFILE_180P, false); // Earlier than 2.3.0
surfaceView?.tag = uid // for mark purpose
val audioManager: AudioManager =
this#LiveStreamingActivity.getSystemService(Context.AUDIO_SERVICE) as AudioManager
//audioManager.mode = AudioManager.MODE_IN_CALL
val isConnected: Boolean = audioManager.isWiredHeadsetOn
if (isConnected) {
/* audioManager.isSpeakerphoneOn = false
audioManager.isWiredHeadsetOn = true*/
mRtcEngine?.setEnableSpeakerphone(false)
mRtcEngine?.setDefaultAudioRoutetoSpeakerphone(false)
mRtcEngine?.setSpeakerphoneVolume(0)
mRtcEngine?.enableInEarMonitoring(true)
// Sets the in-ear monitoring volume to 50% of original volume.
mRtcEngine?.setInEarMonitoringVolume(200)
mRtcEngine?.adjustPlaybackSignalVolume(200)
} else {
/* audioManager.isSpeakerphoneOn = true
audioManager.isWiredHeadsetOn = false*/
mRtcEngine?.setEnableSpeakerphone(true)
mRtcEngine?.setDefaultAudioRoutetoSpeakerphone(true)
mRtcEngine?.setSpeakerphoneVolume(50)
mRtcEngine?.adjustPlaybackSignalVolume(50)
mRtcEngine?.enableInEarMonitoring(false)
// Sets the in-ear monitoring volume to 50% of original volume.
mRtcEngine?.setInEarMonitoringVolume(0)
}
Log.e("", "")
}
#Throws(Exception::class)
private fun joinChannel() {
mRtcEngine?.joinChannel(
null,
AgoraConstants.CHANNEL_NAME,
"Extra Optional Data",
0
) // if you do not specify the uid, we will generate the uid for you
}
#Throws(Exception::class)
private fun leaveChannel() {
mRtcEngine!!.leaveChannel()
}
I think first you want to put setupRemoteVideo in onFirstRemoteVideoDecoded callback instead of the onUserJoined callback. Also, in the onDestroy callback, you should call RtcEngine.destroy() instead of RtcEngine.destroy(mRtcEngine).

Categories

Resources