Android NFC in Embarcadero XE5 - android

Trying to get NFC to work on Android in Embarcadero XE5.
Started with the following: https://forums.embarcadero.com/thread.jspa?threadID=97574
which seem to be working. Now would like to register callback for the NFC Intent
Java approach:
1. Register current activity as a listener
...
2. Receive Intent
#Override
protected void onNewIntent(Intent intent) {
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
        NdefMessage[] msgs = NfcUtils.getNdefMessages(intent);
    }
}
Source: http://www.jessechen.net/blog/how-to-nfc-on-the-android-platform/
Delphi approach (as I would imagine):
1. Define methods available in Java interface
Source: https://forums.embarcadero.com/thread.jspa?messageID=634212
Question:
How do I register a listener for NFC intent messages and
how do I eventually get messages?
My guess would be to call enableForegroundDispatch method. Define it like:
procedure enableForegroundDispatch; cddcl;
to call it from Android API
But since I have never done this before, I do not know how to proceed

Edit : Seems I have missed a tag and the OP does not ask for Java code. Leaving this anyway for future reference
Your guess is correct, although it might be possible to define the intents you want to listen for in the AndroidManifest.xml, foreground dispatch really puts your app in the front, giving you the ability to capture all launched NFC intents.
The way it is described in the docs gives you a clue.
I assume you're familiar with the lifecycle of the Android Activities, Intent Dispatching and such.
Structure
Using the following structure, you'd have 4 fields :
private PendingIntent pendingIntent;
private IntentFilter[] mIntentFilters;
private String[][] mTechLists;
private NfcAdapter mNfcAdapter;
In the onCreate you'd get :
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
mIntentFilters = new IntentFilter[]{new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED)};
mTechLists = new String[][]{new String[]{Ndef.class.getName()},
new String[]{NdefFormatable.class.getName()}};
}
This does not actually enable the Foreground Dispatch yet, it is just the preparation.
The application would receive the Ndef and NdefFormatable technologies
Why do we subscribe for ACTION_NDEF_DISCOVERED ?
The order in which Android tries to handle the intent is as such :
ACTION_NDEF_DISCOVERED
ACTION_TECH_DISCOVERED
ACTION_TAG_DISCOVERED
So we make sure our app is the first to be looked at by Android.
Enable FGD
Put the following line of code in the onResume method :
if (mNfcAdapter != null) {
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, mIntentFilters, mTechLists);
}
Why is this in the onResume? As the docs state : enableForegroundDispatch() must be called from the main thread and only when the activity is in the foreground (calling in onResume() guarantees this)
This should enable your app to receive the intent, of course, when actually running.
If you want to receive intents while not running, you'd have to go to the AndroidManifest.

Related

How to receive intents flagged FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT

In Android 5.0 onwards, HidService.java includes following function:
private void broadcastReport(BluetoothDevice device, byte[] report, int rpt_size) {
Intent intent = new Intent(BluetoothInputDevice.ACTION_REPORT);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothInputDevice.EXTRA_REPORT, report);
intent.putExtra(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, rpt_size);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
sendBroadcast(intent, BLUETOOTH_PERM);
}
I am not able to find any documentation on this flag in the intent. How should I receive this broadcast intent in my app?
==============
Edited content deleted and formed into new question here
This constant is not documented in the Intent API docs because it is not intended for public use.
Here is a description from the android source code I found that describes it. (line 3018)
FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
If set, when sending a broadcast before boot has completed only registered receivers will be called -- no BroadcastReceiver components will be launched. Sticky intent state will be recorded properly even if no receivers wind up being called. If FLAG_RECEIVER_REGISTERED_ONLY is specified in the broadcast intent, this flag is unnecessary.
This flag is only for use by system sevices as a convenience to avoid having to implement a more complex mechanism around detection of boot completion.
Emphasis mine.

How to use BroadCastReceiver programmatically

How can I register a BroadcastReceiver listening for the "PACKAGE_ADDED" action programmatically? I am trying to do something after a package is installed. I can get it working by registering the receiver in the AndroidManifest.xml, but I need to get it working the other way by programmatically registering it so that it only gets called on .apks installed through my app. I've tried it several different ways, the code below is from this answer https://stackoverflow.com/a/4805733/1024722 but it doesn't work. Any ideas?
private BroadcastReceiver receiver;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
ButterKnife.inject(this);
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.v("test", "received");
}
};
registerReceiver(receiver, intentFilter);
}
EDIT: In my app's activity, I click a button to download the app from the server and then to install it I click the downloaded app in my notification bar. It then presents this screen:
I click "Install" and it installs but doesn't call my onReceive method(unless I register it in the xml). Then it shows this screen:
then I click "done" and it returns to my activity with the "install" button. I am wondering if it's not working because it launches the activities shown in the screenshots, and is therefore not able to call the onReceive method in my receiver since my activity's onPause method has been called and isn't "active" anymore until I click done, which is after the "PACKAGE_ADDED" action gets called.
You need to add the http://developer.android.com/reference/android/content/IntentFilter.html#addDataScheme(java.lang.String):
|intent filter object|.addDataScheme("package");
The PackageManager will only send it to receivers that have that have that intent action AND the data scheme as 'package'.
It sounds like you want to control whether components published in your manifest are active, not dynamically register a receiver (via Context.registerReceiver()) while running.
If so, you can use PackageManager.setComponentEnabledSetting() to control whether these components are active:
http://developer.android.com/reference/android/content/pm/PackageManager.html#setComponentEnabledSetting(android.content.ComponentName, int, int)
Note if you are only interested in receiving a broadcast while you are running, it is better to use registerReceiver(). A receiver component is primarily useful for when you need to make sure your app is launched every time the broadcast is sent.

Refreshing activity on receiving gcm push notification

Update: GCM is deprecated, use FCM
How to refresh activity on receiving gcm push notification if my app is open. I have an activity which contains listview filled with data from the server. I want to refresh my activity (here adding one more item to listview) , if I receive gcm push notification(which also contains some data).
One alternative is to add timer that periodically do server requests and update the list adapter data but I don't want these because it will take much resources.
Do I need to add broadcast receiver which will trigger on receiving gcm push which further request for newer server data and update my activity UI?
Dear commentors, please read the question carefully, I only need to refresh the list (if app is open and that particular activity is open) else no need for same.
Took me a few hours to figure it out. Posting here in case anyone anyone else has the same problem.
The idea is that you have to register your activity as a broadcast receiver. The easiest way to do this is like so:
//register your activity onResume()
#Override
public void onResume() {
super.onResume();
context.registerReceiver(mMessageReceiver, new IntentFilter("unique_name"));
}
//Must unregister onPause()
#Override
protected void onPause() {
super.onPause();
context.unregisterReceiver(mMessageReceiver);
}
//This is the handler that will manager to process the broadcast intent
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Extract data included in the Intent
String message = intent.getStringExtra("message");
//do other stuff here
}
};
The above code goes in the activity that you want to 'listen' for events.
Now, how do we send data to this 'listener'? Go to your push notification handler(or from where you want to update your activity) and when you receive a notification call this function:
// This function will create an intent. This intent must take as parameter the "unique_name" that you registered your activity with
static void updateMyActivity(Context context, String message) {
Intent intent = new Intent("unique_name");
//put whatever data you want to send, if any
intent.putExtra("message", message);
//send broadcast
context.sendBroadcast(intent);
}
When you call the above function, your activity should receive it.
Note: Your activity must be running/open to receive the broadcast intent
Note2: I switched to a library called 'otto'. It does actually the same thing but easier, 'broadcasts events' thoughout the app. Here's a link http://square.github.io/otto/
I'm assuming your GCMBroadcastReceiver is in it's own .java file?
As far as refreshing an activity, I would also like to know the answer to that question.
But for knowing if a particular activity is active or not, meaning on screen just add a boolean (call it something like "active") and set it to true in your activity's onResume() event, and to false in the onPause() event:
protected void onResume()
{
super.onResume();
active = true;;
}
protected void onPause()
{
super.onPause();
active = false;
}
Your active variable would be a boolean which is global or static. This way you know if a particular activity is in "front".
Hope that helps a bit.
The accept answer is indeed correct for the "Refreshing activity on receiving gcm push notification" (I've upvoted it too). But if you only want to update a ListView that's being displayed you don't need a broadcast receiver.
Your GCM listener service can update the database using a ContentProvider rather than inserting a direct sql query.
Then you can rely on the notifyChange method on the ContentResolver to do the trick.
Notify registered observers that a row was updated. To register, call
registerContentObserver(). By default, CursorAdapter objects will get
this notification. If syncToNetwork is true, this will attempt to
schedule a local sync using the sync adapter that's registered for the
authority of the provided uri. No account will be passed to the sync
adapter, so all matching accounts will be synchronized.
If your app is already running then try to override the onNewIntent method
Seems there is an easier way. In the OnMessageReceived method of the GCM Listener, you can just do the update from there instead of sending the notification. You can use the same code you would have used if processing the notification. If you're doing StartActivity from the listener, you have to use the ActivityFlags.NewTask flag.
To sum it up in single sentence:
If you want to refresh activity, broadcast your custom event when notification arrives and register your activity as broadcast receiver of that event
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setAction(Long.toString(System.currentTimeMillis()));
PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

Listening to NFC with a pending intent and an intent

I have an app with a pending intent subscribed to ACTION_NDEF_DISCOVERED and a normal intent subscribed to ACTION_TECH_DISCOVERED.
It looks like I need to have the latter intent, so that my app will come up in the NFC selection action screen.
However, with my pending intent (which is modeled after the API reference code) I have to scan the tag twice for the OnNewIntent to fire.
So a few suspicions arise:
Do I really need two intents to capture both in app scans and out of app scans?
Why is that the pending intent is requiring two scans? Is it because I’m subscribing with it in the manifest and in code as shown in the sample or because I have two NFC intents (albeit at different NFC intent levels) ?
Here's the core question:
How do I make this work so that, outside of the app my app comes up in the NFC actions window and inside the app only scan is required for OnNewIntent to fire?
From your description, it looks like your set-up of the code is mostly OK. One thing you may have missed: when your app is started from the NFC actions window (the app chooser), your app's Activity will be started with onCreate() and you have to retrieve the NFC intent with getIntent(). Only after the enableForegroundDispatch() (with the PendingIntent), new NFC intents will cause onNewIntent() to be called.
You could do it like this:
void onCreate(Bundle savedInstanceState) {
... // set up your Activity
handleNfcIntent(getIntent);
}
void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleNfcIntent(intent);
}
void handleNfcIntent(Intent intent) {
// NFC Intent handling code here
}

How does one listen for progress from Android SyncAdapter?

I recall reading about a broadcast receiver interface from the sync adapter or some ResultReceiver of sync progress events. Is there something built into the SyncAdapter pattern or is it home-grown?
I Have just implemented a Broadcast receiver from a sync adapter and it works like clockwork!
Using a Receiver set as an inner class and calling registerReceiver in onCreate and unregisterReceiver in onDestroy did this for me.
As I have one strategy method to spawn and query a number of threads, All I have at the begining of a SyncAdapter run are:
Intent intent = new Intent();
intent.setAction(ACTION);
intent.putExtra(SYNCING_STATUS, RUNNING);
context.sendBroadcast(intent);
And at the end of the sync run i have:
intent.putExtra(SYNCING_STATUS, STOPPING);
context.sendBroadcast(intent);
Within my Activity, I declare:
onCreate(Bundle savedInstance){
super.onCreate(savedInstance);
SyncReceiver myReceiver = new SyncReceiver();
RegisterReceiver(myReceiver,ACTION);
}
onDestroy(){
super.onPause();
unRegisterReceiver(myReceiver);
}
public class SyncReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if (extras != null) {
//do something
}
}
}
For this scenario you do not need to add your receiver to the manifest file. just use as is!
What works:
The method suggested in a 2010 Google IO session, Developing Android REST client applications is to place columns into your ContentProvider as tags to indicate that a record is being fetched or placed or etc. This allows a per-row spinner (or other visual change) to be placed in your UI. You might do that through a custom CursorAdapter that drives a ListView. Your ContentProvider is on the hook to make the flags change as needed.
What doesn't:
You can also use a SyncStatusObserver -- Which is pretty much useless since it responds to every change of status, not just your specific account/contentauthority pair, and really doesn't tell you much anything at all other than that a change occured. So, you can't tell what is being synced, and you can't distinguish "start of sync event" from "end of sync event". Worthless. :P

Categories

Resources