BroadcastReceiver fires after the activity is already created - android

I want to use a BroadcastReceiver to get permission to communicate with a USB device. I am trying to implement it the same way it is done on android website http://developer.android.com/guide/topics/usb/host.html
It all works, kind of.
But the broadcastReceiver is fireing only after the main activity is created. Which means I am able to communicate with the device only after close the app and open it again (when I don't unregister the broadcastReceiver, when I do I can't communicate at all).
What can be the reason?
My code is like this:
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver()
{
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action))
{
synchronized (this)
{
device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false))
{
if(device != null)
{
//things I do when the permission is granted
}
}
else
{
devMessage = "permission denied for device ";
}
}
}
}
};
The part of the code where I register it:
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext())
{
device = deviceIterator.next();
mUsbManager.requestPermission(device, mPermissionIntent);
}
// ...
if(device!=null)
{
// ...
}
else
{
// ...
}
tv.setText(devMessage);
}
Does anyone know why is this happening, what I might be doing wrong?

You're registering your broadcast receiver in you activity. That means that before you run that activity, you cannot receive broadcasts.
You should probably look at registering a reciever-tag in in you AndroidManifest.xml.
This is the docs for the receiver-tag. This allows you to register receivers without starting your activity.
This part is important:
The <application> element has its own enabled attribute that applies to all application components, including broadcast receivers. The <application> and <receiver> attributes must both be "true" for the broadcast receiver to be enabled. If either is "false", it is disabled; it cannot be instantiated.

Related

Android USB connection established only the second time the app is opened

For my application, I need to establish a connection with an attached Arduino device, here is the code:
public String openConnection(UsbManager manager, Context context) {
// getting the driver with an external library...
UsbSerialDriver driver = availableDrivers.get(0);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
manager.requestPermission(driver.getDevice(), pi);
UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
if (connection == null) {
return "Found a device, but cannot connect";
}
// otherwise, continue and do stuff
}
The problem is that, when a device is attached, the first time I open the app it shows the alert asking for the permission, but if I click "OK", the connection is null, so it returns early. However, the second time it does not ask for any permission but the connection is opened and everything works fine.
Why does this happen?
I know this is not the most correct approach to open an USB connection, but I have other issues that are not inherent to the question, so I'm rather intrested to understand why does this happen than what should I do instead
I'm testing this on Android 8.1.0
Try to previously ask for permission and start the rest of your code from a Broadcast Receiver that listens for the USB permission granted.
This is showed in Google's docs:
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(device != null){
//call method to set up device communication
}
}
else {
Log.d(TAG, "permission denied for device " + device);
}
}
}
}
};
Here's how you register the Broadcast Receiver:
UsbManager mUsbManager = (UsbManager)
getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new
Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
And then you start it all with:
UsbDevice device;
...
mUsbManager.requestPermission(device, mPermissionIntent);
In the way you do it, the device tries to connect to USB even before the permission has been granted and so it fails.

Bluetooth scan returns nothing

I am recently learning Android. I tried to implement the function that searches for Bluetooth devices. I am able to list all the previously paired devices, but I am not able to discover new devices.
Permissions are set in AndroidManifest.xml.
Here is my code:
public void listDiscoverableDevices(boolean status) {
// Create a BroadcastReceiver for ACTION_FOUND
mBluetoothAdapter.cancelDiscovery();
mArrayAdapter.clear();
mDisplay.setText("Search");
mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//BluetoothDevice.
if(BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mArrayAdapter.add(device.getAddress());
} else if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
//mDisplay.setText(action);
mArrayAdapter.add(action);
} else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
//mDisplay.setText(action);
mArrayAdapter.add(action);
}
}
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
if(!mBluetoothAdapter.startDiscovery()) {
mDisplay.setText("Discover failed!");
}
}
It seems to me that the BluetoothDevice.ACTION_FOUND action is never received. I have been struggling with this for two days. Any idea?
It took me two days. Today I finally found out that why my program didn't work the first time. Starting from Android 6.0, you also need either android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_FINE_LOCATION permission to scan for remote Bluetooth devices.
And you may need to go to "Settings"->"Apps"->yourApp->"Permissions" to turn on the location service permission. Or I guess you can also request the permission programmatically.

How to grant permission to open usb device with usb manager? openDevice() always returns null

I want to use a usb device in the following code. It successfully lists the usb devices and iterates over them. In the following code the object "device" is the usbdevice that i need to open. Everything seems Ok except the OpenDevice() method that always returns a null value!
[Activity(Label = "TestApp", MainLauncher = true, Icon = "#drawable/icon")]
[IntentFilter(new[] {UsbManager.ActionUsbDeviceAttached})]
[MetaData(UsbManager.ActionUsbDeviceAttached, Resource = "#xml/device_filter")]
public class MainActivity : Activity
{
int count = 1;
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
UsbManager manager = (UsbManager)GetSystemService(Context.UsbService);
UsbDevice device = null;
foreach (var dev in manager.DeviceList)
{
if (dev.Value.VendorId == 5401)
{
device = dev.Value;
}
}
var connection = manager.OpenDevice(device);
// Read some data! Most have just one port (port 0).
}
The device_filter.xml contains the following lines:
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<usb-device product-id="8704" vendor-id="5401" />
</resources>
When I tried bool hasPermision = manager.HasPermission(device); I saw that hasPermission is false. Could anybody tell me How can I grant permission for opening a usb device in xamarin?
Thanks for any help.
At last I fixed it after trying several suggestions. I found that adding the intent filter in manifest does not solve the permission issue for unknown reasons. It's sounds like a bug of the Xamarin. To tell the truth, It seems that Xamarin usb namespace is too naive, and its better you do not waste your time to use that for USB management and connection. It also has some other annoying limitations. For example look at here. (So I suggest to write low level usb communication codes in java and import the jar file in xamarin by JNI, I tired it and I should say It's a lot easier than it seemed at first time)
To grant the permission of opening the usb device, you have to use the grantPermission() method. The following code shows how to use the method and BroadcastReceiver to pop up a dialog asking the user to let the usb usage.
public class MainActivity : Activity
{
private static String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
UsbDevice device;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
UsbManager manager = (UsbManager)GetSystemService(Context.UsbService);
UsbReciever usbReciever = new UsbReciever();
PendingIntent mPermissionIntent = PendingIntent.GetBroadcast(this, 0, new Intent(
ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
RegisterReceiver(usbReciever, filter);
foreach (var dev in manager.DeviceList)
{
if (dev.Value.VendorId == 8192)
{
device = dev.Value;
}
}
manager.RequestPermission(device, mPermissionIntent);
bool hasPermision = manager.HasPermission(device);
UsbDeviceConnection connection = manager.OpenDevice(device);
if (connection == null)
{
return;
}
Button button = FindViewById<Button>(Resource.Id.MyButton);
}
class UsbReciever : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
String action = intent.Action;
if (ACTION_USB_PERMISSION.Equals(action))
{
lock (this)
{
UsbDevice device = (UsbDevice)intent
.GetParcelableExtra(UsbManager.ExtraDevice);
if (intent.GetBooleanExtra(
UsbManager.ExtraPermissionGranted, false))
{
if (device != null)
{
// call method to set up device communication
}
}
else
{
}
}
}
}
}
}

How to check if a bluetooth device is paired

I'm trying to check if there is a bluetooth device paired when running my app.
In the main activity, I find bluetooth devices and pair to them. In the second activity, I must check if there is a device paired or not.
If a device is conected, it starts automatically sending data, but if there is no conexion, then it simply shows a toast.
I need to do this just when the second activity starts. I found this code, but I don't know how to make it to start when the activity is just created.
public void onCreate() {
//...
IntentFilter filter1 = new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED);
IntentFilter filter2 = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
this.registerReceiver(mReceiver, filter1);
this.registerReceiver(mReceiver, filter2);
}
//The BroadcastReceiver that listens for bluetooth broadcasts
private final BroadcastReceiver BTReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
//Do something if connected
}
else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
//Do something if disconnected
}
}
};
Here is a complete description of the problem, with the correct answer to solve it:
Action to know if there is any bluetooth paired device

NFC broadcastreceiver problem

I want my app to listen to nfc tags only when is activated. For this I tried to register an nfc listener as following, without any success.
IntentFilter filter = new IntentFilter("android.nfc.action.TECH_DISCOVERED");
registerReceiver(nfcTagListener, filter);
BroadcastReceiver nfcTagListener = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Log.d("nfc", "" + tag.getId());
}
}
};
I tried as well to declare the intent in my manifest following the apidemos and works perfectly, it launches my activity and gets the nfc tag id. But this is not what I want, I want to detect the tag id only when I am inside that activity. I am thinking it might be related to the following line included in the api demos. But I dont know how to do that programatically
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/filter_nfc">
Any hint?
Thanks!
Try to use Foreground Dispatch System.
To enable it, on the activity's onCreate method, you should prepare some stuffs:
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
after that, create the IntentFilters (in my example, all actions are handled using Intent Filters):
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("*/*");
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
IntentFilter tech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
try {
tech.addDataType("*/*");
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
IntentFilter[] intentFiltersArray = new IntentFilter[] { tag, ndef, tech };
after that, you'll need a String array to contain supported technologies:
String[][] techList = new String[][] { new String[] { NfcA.class.getName(),
NfcB.class.getName(), NfcF.class.getName(),
NfcV.class.getName(), IsoDep.class.getName(),
MifareClassic.class.getName(),
MifareUltralight.class.getName(), Ndef.class.getName() } };
in the onResume method, you should enable the foreground dispatch method:
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techList);
and disable in onPause:
#Override
protected void onPause() {
super.onPause();
nfcAdapter.disableForegroundDispatch(this);
}
By this way, you have successfully initialized the needed mechanism. To handle a received Intent you should override the onNewIntent(Intent intent) method.
#Override
public void onNewIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
// reag TagTechnology object...
} else if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
// read NDEF message...
} else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
}
}
Note: if you want to handle the intents by only foreground dispatching, do not enable Intent Dispatch System in your manifest file, just give the right permissions to your application there.
I hope that helps.
If you do not want to use foreground mode, your can always programmatically enable or disable intent filters.
The NDEF Tools for Android project has working sample using foreground mode, also detects
NFC device support
NFC enabled / disabled on activity launch, or later changed
NFC push enabled / disabled on activity launch, or later changed

Categories

Resources