Im implementing a simple blutooth classic scan from the official docs on my Redmi Note 4 (Bluetooth 4.1).
However I am not seeing a toast that should appear when the onReceive function on the broadcast receiver is called.
What could be wrong here?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
defaultAdapter = BluetoothAdapter.getDefaultAdapter()
if (defaultAdapter == null) {
toast("no adapter")
return
}
if (!defaultAdapter!!.isEnabled) {
val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBluetoothIntent, REQUEST_ENABLE_BLUETOOTH)
}
searchButton.setOnClickListener { newDevicesList() }
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
Log.i("receiver", "registering receiver")
registerReceiver(receiver, filter)
}
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
toast("maybe i find new device who know")
when (intent.action) {
BluetoothDevice.ACTION_FOUND -> {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
val device: BluetoothDevice? =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
val deviceName = device?.name
val deviceHardwareAddress = device?.address // MAC address
toast("device $deviceName was found")
}
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
context.unregisterReceiver(this)
}
}
if (defaultAdapter!!.isDiscovering) {
toast("scan already in progress")
} else {
toast("starting discovery")
defaultAdapter!!.startDiscovery();
}
}
How do i know that the onReceive function never gets called? cause this toast toast("maybe i find new device who know") never appears.
Related
I am developing an Android app using Kotlin and it is supposed to read the NFC tag, start my app and open the URL inside of the app.
However, whenever I am inside the app and I tap the NFC tag it opens Chrome. It is supposed to open the URL inside of it.
Here is my activity:
class MainActivity : AppCompatActivity() {
var TAG = "NFC supported"
var TAG2 = "NFC enabled"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var nfcAdapter = NfcAdapter.getDefaultAdapter(this)
Log.d("NFC supported", (nfcAdapter != null).toString())
Log.d("NFC enabled", (nfcAdapter?.isEnabled).toString())
if (intent != null) {
processIntent(intent)
}
val intent = Intent(this, javaClass).apply {
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val ndef = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply {
try {
addDataType("*/*") /* Handles all MIME based dispatches.
You should specify only the ones that you need. */
} catch (e: IntentFilter.MalformedMimeTypeException) {
throw RuntimeException("fail", e)
}
}
var intentFiltersArray = arrayOf(ndef)
val techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
}
override fun onNewIntent(intent: Intent?){
super.onNewIntent(intent)
if(intent != null){
processIntent(intent)
}
}
private fun processIntent(checkIntent: Intent) {
if(checkIntent.action == NfcAdapter.ACTION_NDEF_DISCOVERED) {
val rawMessages = checkIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
}
}
}
Any guesses?
You need to use enableForegroundDispatch to receive NFC Intent's when your App is in the foreground, the Manifest Filters is just to tell the System you want your App to be in the list of App to be consider to be launched when it sees a Tag of the correct type and your App is not running (Then the Intent should be processed in onCreate
See https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch
So create the Pending Intent and correct Intent Filers and enable/disable Foreground dispatch in onResume and onPause, once that is done Tag's seen while running will be delivered to onNewIntent
Update:
There looks nothing wrong with the documentation parts, but you still have not quite put it together correct.
So I've taken your code and re-arranged it a bit to show a fully working example (and tested with an NFcA NDEF tag)
import android.app.PendingIntent
import android.content.Intent
import android.content.IntentFilter
import android.nfc.NfcAdapter
import android.nfc.tech.NdefFormatable
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
class MainActivity : AppCompatActivity() {
var TAG = "NFC supported"
var TAG2 = "NFC enabled"
private var nfcAdapter : NfcAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
nfcAdapter = NfcAdapter.getDefaultAdapter(this)
Log.d("NFC supported", (nfcAdapter != null).toString())
Log.d("NFC enabled", (nfcAdapter?.isEnabled).toString())
if (intent != null) {
processIntent(intent)
}
}
override fun onNewIntent(intent: Intent?){
super.onNewIntent(intent)
if(intent != null){
processIntent(intent)
}
}
private fun processIntent(checkIntent: Intent) {
if(checkIntent.action == NfcAdapter.ACTION_NDEF_DISCOVERED) {
val rawMessages = checkIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
Log.d("processIntent", "NDEF Found")
}
}
public override fun onPause() {
super.onPause()
nfcAdapter?.disableForegroundDispatch(this)
}
public override fun onResume() {
super.onResume()
val intent = Intent(this, javaClass).apply {
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val ndef = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply {
try {
addDataType("*/*") /* Handles all MIME based dispatches.
You should specify only the ones that you need. */
} catch (e: IntentFilter.MalformedMimeTypeException) {
throw RuntimeException("fail", e)
}
}
var intentFiltersArray = arrayOf(ndef)
val techListsArray = arrayOf(arrayOf<String>(NdefFormatable::class.java.name))
nfcAdapter?.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray)
}
}
Note I changed from NfcF to NdefFormatable::class.java.name in the techlist as this is more useful if only dealing with NDEF type data (As NFcF type tags are less common) but really if the NFC Tag has NDEF data on it the this can valve can be set to null in the enableForegroundDispatch call
could not find any reference on using kotlin to detect incoming call using BroadcastReceiver created dynamically. most java program add intent in the manifest. I wanted to use Broadcastreciever in the specific activity. I used below code but not working. Can anyone help?
Problem:
1. How to set up the Intent Filter for the Phone_State
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
try {
println("Receiver start")
val state = intent!!.getStringExtra(TelephonyManager.EXTRA_STATE)
val incomingNumber =
intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)
if (state == TelephonyManager.EXTRA_STATE_RINGING) {
Toast.makeText(applicationContext, "Incoming Call State", Toast.LENGTH_SHORT).show()
Toast.makeText(applicationContext,
"Ringing State Number is -$incomingNumber",
Toast.LENGTH_SHORT
).show()
}
if (state == TelephonyManager.EXTRA_STATE_OFFHOOK) {
Toast.makeText(applicationContext, "Call Received State", Toast.LENGTH_SHORT).show()
}
if (state == TelephonyManager.EXTRA_STATE_IDLE) {
Toast.makeText(applicationContext, "Call Idle State", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
// Set When broadcast event will fire.
val filter = IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)
LocalBroadcastManager.getInstance(this).registerReceiver(broadCastReceiver, filter)
}
}
I want to use my BroadcastReceiver as sender of data into my activity. For this reason I'm using LocalBroadcastManager. This manager is used to register and unregister my receiver. Problem is that Context in onReceive method is different than Context in onStart and onStop method.
I need to pass activity context into my BroadcastReceiver or instance of LocalBroadcastManager initialized inside Activity. Because my receiver is not receiving any data.
Maybe it is not fault of this manager context but I don't know why it doesnt work since I implemented this manager.
class GPSReceiver: BroadcastReceiver(){
companion object{
const val GPS_PAYLOAD = "gps_payload"
}
override fun onReceive(context: Context, intent: Intent) {
try {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val int = Intent(GPS_PAYLOAD)
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
int.putExtra(GPS_PAYLOAD, true)
} else {
int.putExtra(GPS_PAYLOAD, false)
}
LocalBroadcastManager.getInstance(context).sendBroadcast(int)
} catch (ex: Exception) {
}
}
}
Registering receiver inside Activity:
private val gpsStatusReceiver = object: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
App.log("isGpsEnabled: onReceive")
val gpsStatus = intent?.extras?.getBoolean(GPS_PAYLOAD)
if (gpsStatus != null) {
if (gpsStatus){
App.log("isGpsEnabled: true")
hideGpsSnackbar()
} else {
App.log("isGpsEnabled: false")
showGpsSnackbar()
}
} else {
App.log("isGpsEnabled: null")
}
}
}
override fun onStart() {
super.onStart()
LocalBroadcastManager.getInstance(this).apply {
val filter = IntentFilter()
filter.apply {
addAction("android.location.PROVIDERS_CHANGED")
addAction(GPS_PAYLOAD)
}
registerReceiver(gpsStatusReceiver, filter)
}
}
I have seen your code. So there is not issue with context, but in the approach.
Your are registering your reciever with the same strings in which you are getting you data inside the Reciever.
So Send Your broadcast from Fragment/Activity
Send BroadCast Like
private fun sendSuccessfulCheckoutEvent() {
val intent = Intent("successful_checkout_event")
intent.putExtra("cartID", cartId)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
And Listen it in Activity/Fragment like this
1) Create broadcast Reciever
private val checkoutDoneReciever : BroadcastReceiver = object : BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
val cartNumbers = intent.getIntExtra("cartID", 0)
Log.d("receiver", "Got message: $cartNumbers.toString()")
}
}
2) Register it in onCreate()/onStart()
LocalBroadcastManager.getInstance(this).registerReceiver(cartUpdatedReceiver,IntentFilter("successful_checkout_event"))
3) Unregister it in onDestroy()
LocalBroadcastManager.getInstance(this).unregisterReceiver(cartUpdatedReceiver)
I have a small bluetooth project in kotlin. The code for it is below:
var mArrayAdapter: ArrayAdapter<String>? = null
val bluetoothAdapter: BluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
var myList: MutableList<String> = mutableListOf<String>()
var devices = ArrayList<BluetoothDevice>()
var devicesMap = HashMap<String, BluetoothDevice>()
class MainActivity : AppCompatActivity(), View.OnClickListener {
private val REQUEST_ENABLE_BT = 1000
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<View>(R.id.btn_scan).setOnClickListener(this)
checkBluetoothStatus()
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
registerReceiver(receiver, filter)
mArrayAdapter = ArrayAdapter(this, R.layout.dialog_select_device)
}
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action: String? = intent.action
when(action) {
BluetoothDevice.ACTION_FOUND -> {
val device: BluetoothDevice =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
val deviceName = device.name
myList.add(deviceName)
}
}
}
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btn_scan ->
startScan()
}
}
fun startScan() {
if (BluetoothAdapter.getDefaultAdapter().startDiscovery()) {
val myToast = Toast.makeText(this, myList.toString(), Toast.LENGTH_SHORT)
myToast.show()
}
}
}
When a user clicks a button, I want to be able to see a list of other bluetooth devices (which I thought the onReceive method would enable me to do). So I'm adding device names to the myList variable, which I'm then displaying in a toast. But at the moment when they click, nothing comes up. Grateful for your advice.
I need to scan for a list of wifi access points in my android app. I've done it in the past using java, but I'm having trouble getting my kotlin code to work.
My code:
var resultList = ArrayList<ScanResult>()
lateinit var wifiManager: WifiManager
val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
resultList = wifiManager.scanResults as ArrayList<ScanResult>
Log.d("TESTING", "onReceive Called")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
wifiManager = this.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
}
override fun onGridTileClicked(x: Int, y: Int) {
startScanning()
}
fun startScanning() {
registerReceiver(broadcastReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
Handler().postDelayed({
stopScanning()
}, 10000)
}
fun stopScanning() {
unregisterReceiver(broadcastReceiver)
val axisList = ArrayList<Axis>()
for (result in resultList) {
axisList.add(Axis(result.BSSID, result.level))
}
Log.d("TESTING", axisList.toString())
}
The onReceive() function is never called, and I have the ACCESS_FINE_LOCATION and ACCESS_WIFI_STATE both declared in the manifest, so I'm not sure what I'm doing wrong. I'm sure I'm missing something obvious, but some help would be appreciated. Thanks!
You forgot to start scanning. Add wifiManager.startScan() call in your startScanning method.