I want to connect multiple Android devices via Network Service Discovery. I want to use a star topology. so on device should discover and the others a registering a service which should found by the discoverer. For this I took the NSD Chat (https://github.com/gauravcanon/NsdChat ) NsdHelper and changed it only a bit, so a callback gets called after resolving a service which establish a connection. This works in mostly if I am using three devices. two advertiser and one discoverer. The services getting resolved and a connection estabished. If I get an additional third advertiser it is crashing in 80% of all attempts. The reason is that the serviceInfo which is passed in the onServiceResolved function in the ResolveListener contains the ip address of the resolving phone and not of the advertising one. The port is correct and the servicename also. That is such a strange behaviour, I don't know how to debug this. I'm using the bonjour browser to see all registered services and the registration of all services is fine. All service infos containing the right ip address and port. I also tried the the pure discovering process without establishing a connection. Same failure. Sometimes the ip address of the resolver is in the serviceinfo. It can also happen at the first discovery and the second, but most likely it is on the third one.
I will post my NsdHelper code below. I made some edits right now because i tried to start the discovery process again after resolving, so there are more differences to the NSD Chat, but the error persists.
Is someone using the NSD implementation of Android via the NSDManager with multiple devices? Is it working for you? What are you doing different?
This topic is related there someone had the same problems ( Android, NSD/DNS-SD: NsdManager unreliable discovery and IP resolution ). I cant imagine that this error is still a thing 3 years ago?
I'm thankful for every hint!
class NsdHelper(private var mContext: Context, private val createClient: (InetAddress, Int)->Unit, val mHandler: Handler) {
internal var mNsdManager: NsdManager = mContext.getSystemService(Context.NSD_SERVICE) as NsdManager
internal lateinit var mResolveListener: NsdManager.ResolveListener
private var mDiscoveryListener: NsdManager.DiscoveryListener? = null
private var mRegistrationListener: NsdManager.RegistrationListener? = null
var mServiceName = "Wizard"
var chosenServiceInfo: NsdServiceInfo? = null
internal set
val mServices = mutableListOf<NsdServiceInfo>()
fun initializeNsd() {
stopDiscovery()
tearDown()
initializeResolveListener()
}
fun reset(){
initializeResolveListener()
discoverServices()
}
fun initializeDiscoveryListener() {
mDiscoveryListener = object : NsdManager.DiscoveryListener {
override fun onDiscoveryStarted(regType: String) {
Log.d(TAG, "Service discovery started")
}
override fun onServiceFound(service: NsdServiceInfo) {
Log.d(TAG, "Service discovery success$service")
when {
service.serviceType != SERVICE_TYPE -> Log.d(TAG, "Unknown Service Type: " + service.serviceType)
service.serviceName == mServiceName -> Log.d(TAG, "Same machine: $mServiceName")
service.serviceName.contains("Verzauberte") -> {
if (mServices.none { it.serviceName == service.serviceName })
mNsdManager.resolveService(service, mResolveListener)
}
}
}
override fun onServiceLost(service: NsdServiceInfo) {
Log.e(TAG, "service lost$service")
}
override fun onDiscoveryStopped(serviceType: String) {
Log.i(TAG, "Discovery stopped: $serviceType")
}
override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
Log.e(TAG, "Discovery failed: Error code:$errorCode")
}
override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
Log.e(TAG, "Discovery failed: Error code:$errorCode")
}
}
}
fun initializeResolveListener() {
mResolveListener = object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
Log.e(TAG, "Resolve failed$errorCode")
}
override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
Log.e(TAG, "Resolve Succeeded. $serviceInfo")
if (serviceInfo.serviceName == mServiceName) {
Log.d(TAG, "Same IP.")
return
}
chosenServiceInfo = serviceInfo
mHandler.post(Runnable {
createClient(
serviceInfo.host,
serviceInfo.port
)
})
mServices.add(serviceInfo)
reset()
}
}
}
fun initializeRegistrationListener() {
mRegistrationListener = object : NsdManager.RegistrationListener {
override fun onServiceRegistered(NsdServiceInfo: NsdServiceInfo) {
mServiceName = NsdServiceInfo.serviceName
Log.d(TAG, "Service registered: $mServiceName")
}
override fun onRegistrationFailed(arg0: NsdServiceInfo, arg1: Int) {
Log.d(TAG, "Service registration failed: $arg1")
}
override fun onServiceUnregistered(arg0: NsdServiceInfo) {
Log.d(TAG, "Service unregistered: " + arg0.serviceName)
}
override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
Log.d(TAG, "Service unregistration failed: $errorCode")
}
}
}
fun registerService(port: Int) {
tearDown() // Cancel any previous registration request
initializeRegistrationListener()
val serviceInfo = NsdServiceInfo().apply {
serviceType = SERVICE_TYPE
serviceName = "Verzauberte[$port]"
setPort(port)
}
mNsdManager.registerService(
serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener
)
}
fun discoverServices() {
stopDiscovery() // Cancel any existing discovery request
initializeDiscoveryListener()
Log.d(this.toString(), "Start discovering")
mNsdManager.discoverServices(
SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener
)
}
fun stopDiscovery() {
if (mDiscoveryListener != null) {
try {
mNsdManager.stopServiceDiscovery(mDiscoveryListener)
} finally {
}
mDiscoveryListener = null
}
}
fun tearDown() {
if (mRegistrationListener != null) {
try {
mNsdManager.unregisterService(mRegistrationListener)
} finally {
}
mRegistrationListener = null
}
}
companion object {
val SERVICE_TYPE = "_votinginteractions._tcp."
val TAG = "NsdHelper"
}
}
Related
I have an application that sends and receives data using BLE. Originally it was classical bluetooth but I was tasked with changing the project to BLE.
So far I have succeeded in sending data but not receiving it. The main activity contains multiple fragments. On of those is responsible for sending data where as the other sends a request and then receives a response with the data from the BLE device.
one fragment is called Parameter and the other Memory. Each fragment has a viewmodel and repository as the architecture is based on MVVM. the flow is as follows:
Parameter fragment -> View model -> repository -> DataStore class -> DataStore uses instance from BLEConnectionManager class to send the data of the corresponding parameter. Example of a function in DataStore:
fun sendToolAddressParam(data: Int){
toolAddress = data
var value = Integer.toHexString(data)
if (value.length == 1) value = "0$value"
val message = WriteCommandCodes.TOOL_ADDRESS.value + " " + value + " " + WriteCommandCodes.EXTRA2.value
BleConnectionManager.sendMessage(message)
Timber.i("Payload: $message")
}
There are also functions that request data:
fun requestToolAddress(){
BleConnectionManager.requestReadValues(ReadRequestCodes.TOOL_ADDRESS.value)
}
in the BLE class the functions are written as the following:
fun write(message:String){
val bytes = BigInteger(message.replace("\\s".toRegex(), ""), 16).toByteArray()
Timber.i("Bytes value ---> ${bytes.toHexString()}")
val device = getBleDevice()
// val characteristicRX = getBleCharacteristic()
val characteristicRX = bluetoothGattRef.getService(XpressStreamingServiceUUID).getCharacteristic(
peripheralRX)
writeCharacteristic(device, characteristicRX, bytes)
}
fun requestReadValues(requestCode:String){
if(isConnected.value!!){
write(requestCode)
}else{
Timber.e("Make sure that you connected and paired with the desired device.")
}
}
fun sendMessage(message:String){
Timber.i("Check if isConnected = true --> ${isConnected.value}")
if(isConnected.value == true){
write(message)
}else{
Timber.e("Make sure that you connected and paired with the desired device.")
}
}
Now here is my issue I want to receive data from the BLE device after I send the request, the device's documentation when it comes to BLE data exchange is here: https://docs.silabs.com/gecko-os/1/bgx/latest/ble-services
Now I have a function that supposedly receives the incoming messages but this was when classical bluetooth was used.
fun readIncomingMessages(message: String){
when{
message.startsWith(com.brainsocket.milink.data.bluetooth.ReadResponseCodes.KEY_ADDRESS.value) ->{
EventBus.getDefault().post(
ReadKeyAddressEvent(message.substring(com.brainsocket.milink.data.bluetooth.ReadResponseCodes.KEY_ADDRESS.value.length+1, com.brainsocket.milink.data.bluetooth.ReadResponseCodes.KEY_ADDRESS.value.length+3))
)
Timber.i("Message received: $message")
}
message.startsWith(com.brainsocket.milink.data.bluetooth.ReadResponseCodes.TOOL_ADDRESS.value) ->{
EventBus.getDefault().post(
ReadToolAddressEvent(message.substring(com.brainsocket.milink.data.bluetooth.ReadResponseCodes.TOOL_ADDRESS.value.length+1, com.brainsocket.milink.data.bluetooth.ReadResponseCodes.TOOL_ADDRESS.value.length+3))
)
Timber.i("Message received: $message")}
message.startsWith(com.brainsocket.milink.data.bluetooth.ReadResponseCodes.RPM_THRESHOLD.value) ->{
EventBus.getDefault().post(
ReadRPMThresholdEvent(message.substring(com.brainsocket.milink.data.bluetooth.ReadResponseCodes.RPM_THRESHOLD.value.length+1, com.brainsocket.milink.data.bluetooth.ReadResponseCodes.RPM_THRESHOLD.value.length+3))
)
Timber.i("Message received: $message")}
message.startsWith(com.brainsocket.milink.data.bluetooth.ReadResponseCodes.BACKLASH.value) ->{
EventBus.getDefault().post(
ReadBacklashEvent(message.substring(com.brainsocket.milink.data.bluetooth.ReadResponseCodes.BACKLASH.value.length+1, com.brainsocket.milink.data.bluetooth.ReadResponseCodes.BACKLASH.value.length+6))
)
Timber.i("Message received: $message")}
As you can see the Event Bus is used here, it is also used here in the DataStore:
#Subscribe(threadMode = ThreadMode.MAIN)
fun onKeyAddressEvent(event: ReadKeyAddressEvent) {
Timber.i("onKeyAddressEvent: data:${event.data}")
keyAddress = Integer.parseInt(event.data , 16)
EventBus.getDefault().post(ReadMemoryItemsEvent())
}
#Subscribe(threadMode = ThreadMode.MAIN)
fun onToolAddressEvent(event: ReadToolAddressEvent) {
Log.d(LOG_TAG, "onToolAddressEvent: data:${event.data}")
when(Integer.parseInt(event.data , 16)){
0 -> toolAddress = 1
1 -> toolAddress = 2
}
EventBus.getDefault().post(ReadMemoryItemsEvent())
}
#Subscribe(threadMode = ThreadMode.MAIN)
fun onRPMThresholdEvent(event: ReadRPMThresholdEvent) {
Log.d(LOG_TAG, "onRPMThresholdEvent: data:${event.data}")
rpmThreshold = Integer.parseInt(event.data , 16)
EventBus.getDefault().post(ReadMemoryItemsEvent())
}
#Subscribe(threadMode = ThreadMode.MAIN)
fun onReadBacklashEvent(event: ReadBacklashEvent) {
Log.d(LOG_TAG, "onReadBacklashEvent: data:${event.data}")
val data = event.data
backlash = parseGotoPos(data)
EventBus.getDefault().post(ReadMemoryItemsEvent())
}
This is in the repository:
fun getMemoryItems() : List<ModesPosItem> = listOf(
ModesPosItem(value = btDataStore.keyAddress, title = context.getString(R.string.key_address_string)),
ModesPosItem(value = btDataStore.toolAddress, title = context.getString(R.string.tool_address_string)),
ModesPosItem(value = btDataStore.rpmThreshold, title = context.getString(R.string.rpm_threshold_string)),
ModesPosItem(value = btDataStore.backlash, title = context.getString(R.string.backlash_string))
)
This is in the viewmodel:
#Subscribe(threadMode = ThreadMode.MAIN)
fun onReadMemoryItemsEvent(event: ReadMemoryItemsEvent) {
memoryItems.value = repository.getMemoryItems()
Timber.i("Memory Items [tool address, keyAddress, RPM threshold, backlash]: ${memoryItems.value.toString()}")
}
This is in the fragment:
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
What exactly am I supposed to do to acquire the data from the BLE device?
I made an app that write and read continous stream data throw BLE connection from a bluetooth device.
The Flow i follow is the following:
Connect Gatt;
Discover Services;
Write To Characteristic;
Subscribe to Notification;
Read Characteristic from notification --> Here the EventBus post() with your data package;
Going deeper into the connection and using some code:
After you connect to the GATT you call onConnectionStateChange to listen for changes in the gatt connection:
private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
val deviceAddress = gatt.device.address
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.w("BluetoothGattCallback", "Successfully connected to $deviceAddress")
// NOW DISCOVER SERVICES
gatt.discoverServices()
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.w("BluetoothGattCallback", "Successfully disconnected from $deviceAddress")
}
} else {
Log.w(
"BluetoothGattCallback",
"Error $status encountered for $deviceAddress! Disconnecting..."
)
}
If the GATT is connected succesfully it will discover services.
At this step you can write to the characteristic as follow:
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
with(gatt) {
Log.w(
"BluetoothGattCallback",
"Discovered ${services.size} services for ${device.address}"
)
val msg = byteArrayOf(0x00.toByte())
val newcharacteristic = gatt!!.getService(dataUUID_service).getCharacteristic(
dataUUID_characteristic
)
newcharacteristic!!.value = msg
gatt!!.writeCharacteristic(newcharacteristic)
}
}
This will let you go on the next step, the onCharacteristicWrite listener:
override fun onCharacteristicWrite(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
val characteristic = gatt.getService(dataUUID_service).getCharacteristic(
dataUUID_characteristic
)
gatt.setCharacteristicNotification(characteristic, true)
val descriptor = characteristic!!.getDescriptor(descriptor_UUID)
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
if (descriptor != null) {
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
gatt.writeDescriptor(descriptor)
}
}
Writing the characteristic will let you go into the onCharacteristicChanged listener that will give you back the data from the ble device and in which you can use the event bus to use your data.
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
charac: BluetoothGattCharacteristic
) {
super.onCharacteristicChanged(gatt, charac)
// Log.d("CHARAC", "Characteristic Changed")
onCharacteristicRead(gatt, charac, BluetoothGatt.GATT_SUCCESS)
}
Where onCharacteristicRead should look like:
override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
with(characteristic) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Log.i("BluetoothGattCallback","Read characteristic $uuid:\n${value.toHexString()}" )
// value is the read value from ble device
// HERE YOU HANDE YOUR EVENT BUS, example:
val eventData: deviceListener = deviceListener(value)
EventBus.getDefault().post(eventData)
}
BluetoothGatt.GATT_READ_NOT_PERMITTED -> {
Log.e("BluetoothGattCallback", "Read not permitted for $uuid!")
}
else -> {
Log.e(
"BluetoothGattCallback",
"Characteristic read failed for $uuid, error: $status"
)
}
}
}
}
Maybe it is not the most efficent way and not the clearest code but it works like a charm.
I am trying to listen to network changes using method registerDefaultNetworkCallback() of conenctivityManager
Using the code below from this answer
val connectivityManager = cotnext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager?.let {
it.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
//take action when network connection is gained
}
override fun onLost(network: Network) {
//take action when network connection is lost
}
})
}
but I have a few questions about this method:
what if the phone is connected to wifi but the wifi is not connected to Internet
In the method documentation I read this which I don't understand, when exactly will the limit will hit? If the callback is called 100 times then an Exception will be thrown? And how to handle this?
To avoid performance issues due to apps leaking callbacks, the system will limit the number of outstanding requests to 100 per app (identified by their UID), shared with all variants of this method, of requestNetwork as well as ConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback. Requesting a network with this method will count toward this limit. If this limit is exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources, make sure to unregister the callbacks with unregisterNetworkCallback(ConnectivityManager.NetworkCallback).
what if the phone is connected to wifi but the wifi is not connected
to Internet
The answer, this method will return false
In the method documentation I read this which I don't
understand, when exactly will the limit will hit? If the callback is
called 100 times then an Exception will be thrown? And how to handle
this?
I think it means if you cant register more than 100 callback
At first, add the ConnectivityReceiver class:
class ConnectivityReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (connectivityReceiverListener != null) {
connectivityReceiverListener!!.onNetworkConnectionChanged(
isConnectedOrConnecting(
context
)
)
}
}
private fun isConnectedOrConnecting(context: Context): Boolean {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (cm != null) {
if (Build.VERSION.SDK_INT < 23) {
val ni = cm.activeNetworkInfo
if (ni != null) {
return ni.isConnected && (ni.type == ConnectivityManager.TYPE_WIFI || ni.type == ConnectivityManager.TYPE_MOBILE)
}
} else {
val n = cm.activeNetwork
if (n != null) {
val nc = cm.getNetworkCapabilities(n)
return nc!!.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || nc!!.hasTransport(
NetworkCapabilities.TRANSPORT_WIFI
)
}
}
}
return false
}
interface ConnectivityReceiverListener {
fun onNetworkConnectionChanged(isConnected: Boolean)
}
companion object {
var connectivityReceiverListener: ConnectivityReceiverListener? = null
}
}
Then In your BaseActivity or MainActivity add these lines:
abstract class BaseActivity:AppCompatActivity(),
ConnectivityReceiver.ConnectivityReceiverListener {
var receiver: ConnectivityReceiver? = null
override fun onResume() {
super.onResume()
try {
receiver = ConnectivityReceiver()
registerReceiver(
receiver!!,
IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
)
connectivityReceiverListener = this
} catch (ex: Exception) {
//Timber.d("Base ex ${ex.localizedMessage}")
}
}
override fun onPause() {
try {
unregisterReceiver(receiver!!)
receiver = null
} catch (ex: Exception) {
}
super.onPause()
}
override fun onNetworkConnectionChanged(isConnected: Boolean) {
showMessage(isConnected)
}
private fun showMessage(isConnected: Boolean) {
try {
if (!isConnected) {
Log.d("Connection state"," disconnected")
} else {
Log.d("Connection state"," connected")
}
} catch (ex: Exception) {
}
}
}
You should register the receiver in the OnResume method and unregister it in theOnPause method
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>
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).
I need to connect to a bluetooth device which acts as a server. I know its UUID (at least the device's documentation contains it). However, I get an exception when I try to connect to it. The discovery part takes place successfully.
In the following, I cite the relevant code parts.
Here is the discovery. After I successfully found my device, I try to connect to it.
private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
private val bluetoothReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action: String = intent.action
when (action) {
BluetoothDevice.ACTION_FOUND -> {
val foundDevice: BluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
Log.i("NAME", foundDevice.name)
if (foundDevice.name.startsWith("RN487")) {
bluetoothAdapter?.cancelDiscovery()
device = foundDevice
val connectThread = ConnectThread(device)
connectThread.start()
}
}
}
}
}
private lateinit var device: BluetoothDevice
The ConnectThread class is here:
private inner class ConnectThread(device: BluetoothDevice) : Thread() {
private val mSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
device.createRfcommSocketToServiceRecord(UUID)
}
override fun run() {
bluetoothAdapter?.cancelDiscovery()
mSocket?.use { socket ->
socket.connect()
toast("Connected!")
}
}
fun cancel() {
try {
mSocket?.close()
} catch (e: IOException) {
Log.e(TAG, "Could not close the client socket", e)
}
}
}
The UUID was given as
private val UUID = nameUUIDFromBytes("49535343-...".toByteArray())
Thanks for your time and expertise!
As one of my eagle-eyed colleagues pointed out, the bluetooth description begins with the "oldschool" version on the official android developers site. Later, the bluetooth low energy is described, which I need for my project.