Send NDEF message from Android device using Xamarin - android

I want to send an NDEF message from one Android device to other device. Not to write in a tag, but send the message to the nearby device.
I'm using Xamarin.Android to develop the Android app and I create the message like this:
NdefRecord uriRecord = NdefRecord.CreateUri("http://myURL");
NdefMessage message = new NdefMessage(new[] { uriRecord });
I now want to send the message when nearby device is detected, but I don't know how it is done. Xamarin's documentation is not complete, and I'm not familiar with Android development.
Could anyone help me or show a simple example?

I figured out.
The main activity has to implement NfcAdapter.ICreateNdefMessageCallback and NfcAdapter.IOnNdefPushCompleteCallback interfaces.
Implement CreateNdefMessage and OnNdefPushComplete methods.
Call SetNdefPushMessageCallback and SetOnNdefPushCompleteCallback methods of NfcAdapter in the OnCreate method of the main activity.
public class Activity1 : Activity, NfcAdapter.ICreateNdefMessageCallback, NfcAdapter.IOnNdefPushCompleteCallback
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
...
NfcAdapter adapter = NfcAdapter.GetDefaultAdapter(this);
adapter.SetNdefPushMessageCallback(this, this);
adapter.SetOnNdefPushCompleteCallback(this, this);
}
public NdefMessage CreateNdefMessage(NfcEvent e)
{
NdefRecord uriRecord = NdefRecord.CreateUri("http://myURL");
NdefMessage message = new NdefMessage(new[] { uriRecord });
return message;
}
public void OnNdefPushComplete(NfcEvent e)
{
//throw new NotImplementedException();
}
}

Related

Cast custom channel callback never called

I'm working on an Android app that communicates with a Cast receiver app.
Connecting to the app works (I can see the app appear on the tv), but I'm having difficulties getting the custom channel to work.
In the onCreate of my Activity I get the CastContext and add my SessionManagerLister.
mCastContext = CastContext.getSharedInstance(this);
mCastContext.getSessionManager().addSessionManagerListener(getSessionManagerListener(), CastSession.class);
getSessionManagerListener() returns the listener where I register my MessageReceivedCallback:
private SessionManagerListener<CastSession> getSessionManagerListener()
{
return new SessionManagerListener<CastSession>()
{
#Override
public void onSessionStarted(CastSession castSession, String s)
{
try
{
castSession.setMessageReceivedCallbacks("urn:x-cast:be.myappname.player.cast.v1", new Cast.MessageReceivedCallback()
{
#Override
public void onMessageReceived(CastDevice castDevice, String s, String s1)
{
System.out.println("never reaches this callback");
}
});
}
catch (IOException e)
{
e.printStackTrace();
}
}
... other methods omitted ...
}
}
When I tap the Toolbar cast button I can select a device, which triggers the onSessionStarted in the SessionManagerListener (this also starts the receiver app on the tv). I then add the MessageReceivedCallback, but its callback never gets called.
Inspecting my Cast device in Chrome does show the data I'm expecting to receive, it just never seems to reach my Android code.
cast_receiver.js:67 [667.202s] [cast.receiver.IpcChannel] IPC message
[667.202s] [cast.receiver.IpcChannel] IPC message sent: {"namespace":"urn:x-cast:be.myappname.player.cast.v1","senderId":"7c442884-74e6-a388-243c-58b4ab3a4527.3471:com.google.sample.cast.refplayer.tutorial-512","data":"{\"type\":\"login request\"}"}
A colleague is working on the iOS app and that one does receive the callback.
Try the following in onSessionStarted
CastContext cc = CastContext.getSharedInstance(this);
SessionManager sm = cc.getSessionManager();
if (sm != null) {
CastSession cs = sm.getCurrentCastSession();
if (cs != null) {
try {
MyCastChannel mcc = new MyCastChannel();
cs.setMessageReceivedCallbacks("urn:x-cast:be.myappname.player.cast.v1",mcc);
}
catch (IOException e) {
}
}
}
public class MyCastChannel implements Cast.MessageReceivedCallback
{
#Override
public void onMessageReceived(CastDevice castDevice, String namespace, String message)
{
// do your thing
}
}
I had the same problem, this is how I managed to get the message to be sent:
context.sendCustomMessage(namespace, undefined, JSON.stringify({
"a": "b"
}));
This is the javascript on the receiver side. So you need the "undefined" param and also use JSON.stringify(), otherwise the message gets silently dropped.
The undefined means "send to all", but you should probably specify sender-id there.
This is in the v3 API.
In my case, it was more subtle.
The callback worked absolutely fine when the cast session was initiated for the first time. When the user presses the cast button the receiver is registered for the message callback.
override fun onSessionStarted(castSession: CastSession?, p1: String?) {
liveViewModel.requestPause()
castSession?.let {
setCastChannelReceiver(it, this#myActivity)
loadRemoteMedia(it, buildChromeCastInfo())
}
}
fun setCastChannelReceiver(castSession: CastSession?, receiver: CastMessageReceiver) {
castSession?.let {
castChannel.addReceiver(receiver, castSession)
it.setMessageReceivedCallbacks(castChannel.nameSpace, castChannel)
}
}
Although when the user use to kill the Activity which initiated the cast session and then after traversing other parts of app use to again visit the Activity, the callback failed to work.
Remember, when the user visits the Activity for the second time, the CastSession is already connected. As a result the onSessionStarted(castSession: CastSession, p1: String) method is never called.
I was under the assumption that once the receiver has been registered for the session, it need not be registered again. But still for some reason the callback never worked.
As a final resort, just to be assured I re-registered the receiver in the OnCreate() of the Activity.
override fun onCreate(out:Bundle){
....
setCastChannelReceiver(castSession, receiver)
....
}
fun setCastChannelReceiver(castSession: CastSession?, receiver: CastMessageReceiver) {
castSession?.let {
castChannel.addReceiver(receiver, castSession)
it.setMessageReceivedCallbacks(castChannel.nameSpace, castChannel)
}
}
And it worked!!
NOTE: For me, the communication between the Sender(Android App) and Cast Receiver only occurred when the string messages were in JSON format.

Unable to both start and discover a specific service with Wifi Direct

I'm pretty new with Android programming. But I have been working on this for over a week now, and it starts to get booooring.
My idea is that I want to connect two devices using Wifi Direct. But I only want to connect to those which are running my application. Besides, I want the users to be able to see some information of the other devices (such as user name), not just the MAC or the Android_XXXX name included in the WifiP2pDevice. That's why I decided that a device looking for other devices, should both start the application service and search for peers which are also broadcasting this service.
The problem (I'm testing with two real devices) is that, even though they are running exactly the same code, only one of them is getting the service discovery callbacks (the onDnsSd... listeners below). So, one side acts in the proper way, but not the other. Moreover I'm getting "old" services, meaning that apparently each time I start de service (even though I cancel previously started services), that service seems to be still broadcast during at least some minutes.
I include a shortened version of my code:
public class MoveFlufietsDialogFragment extends DialogFragment implements ChannelListener, DeviceActionListener {
public final HashMap<String, FlufietsPeer> mBuddies = new HashMap<String, FlufietsPeer>();
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
...
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
mManager = (WifiP2pManager) getActivity().getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(getActivity(), getActivity().getMainLooper(), null);
...
startRegistration();
discoverFlufietsService();
...
}
public void discoverFlufietsService() {
DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
#Override
public void onDnsSdTxtRecordAvailable(String fullDomain, Map record, WifiP2pDevice device) {
// This and the next listener are only called in one of the devices.
String serviceName = (String) record.get("serviceName");
if ((serviceName != null) && (serviceName.equals("flufiets")) {
// I put the record data in the mBuddies HashMap.
...
mBuddies.put(device.deviceAddress, myPeerDataStructure);
}
}
};
DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
#Override
public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice resourceType) {
if (mBuddies.containsKey(resourceType.deviceAddress)) {
FlufietsPeer flufietsPeer = mBuddies.get(resourceType.deviceAddress);
WiFiPeerListAdapter adapter = ((WiFiPeerListAdapter) mFragmentList.getListAdapter());
adapter.add(flufietsPeer);
adapter.notifyDataSetChanged();
}
}
};
mManager.setDnsSdResponseListeners(mChannel, servListener, txtListener);
WifiP2pDnsSdServiceRequest serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
mManager.addServiceRequest(mChannel, serviceRequest, new ActionListener() {
// onSuccess/onFailure toasts.
});
mManager.discoverServices(mChannel, new WifiP2pManager.ActionListener() {
// onSuccess/onFailure toasts.
});
}
public void startRegistration() {
mManager.clearLocalServices(mChannel, new ActionListener() {
// onSuccess/onFailure toasts.
});
Map record = new HashMap();
record.put("serviceName", "flufiets");
...
WifiP2pDnsSdServiceInfo serviceInfo = WifiP2pDnsSdServiceInfo.newInstance(flufietsService, "_tcp", record);
mManager.addLocalService(mChannel, serviceInfo, new ActionListener() {
// onSuccess/onFailure toasts.
});
}
#Override
public void onResume() {
super.onResume();
mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
getActivity().registerReceiver(mReceiver, mIntentFilter);
}
#Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mReceiver);
}
#Override
public void onStop() {
super.onStop();
mManager.clearLocalServices(mChannel, new ActionListener() {
// onSuccess/onFailure toasts.
});
}
...
}
The problem doesn't seem to be related with the device itself (sometimes it works, sometimes it doesn't, but always only in one of them). I suspect it has to do with either trying to discover a service that we ourselves are broadcasting, or having the same service being offered by two devices. I have tried changing the names of the service, so each device would offer either a "send" or "receive" service, but it doesn't work. I only get the callbacks called (onDnsSd...) in one of the devices.
And that thing about getting old services, when I always clear them, is weird (I do include a timestamp in the service record data, and I could always discard all but the last, but doesn't seem to be logical).
Any ideas? ANY help would be VERY appreciated, because writing the application is not funny any more (:-)=
Thanks a lot!
You need to wait until the clearLocalService call succeeds before adding the local service later. So put the addLocalService call into the onSuccess callback of the clearLocalServices.

Android NFC throwing exception

I have an android app using P2P NFC. NFC works but i have to tap the devices twice to start it. When i debug my app and tap devices it calls createNdefMessage function but throws an exception at JavaBinder.
In run mode it doesn't crashes but i have to tap the devices twice to start NFC.
Before NFC i call a file selector to select a file to transfer.
Here is my code OnCreate
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// INFO TEXTVIEW
mInfoText = (TextView) findViewById(R.id.info_text_view);
// FILE SELECTOR BUTTON
mStartActivityButton = (Button)findViewById(R.id.start_file_picker_button);
mStartActivityButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.start_file_picker_button:
// Create a new Intent for the file picker activity
Intent intent = new Intent(getApplicationContext(), FilePickerActivity.class);
// Start the activity
startActivityForResult(intent, REQUEST_PICK_FILE);
break;
}
}
});
// CHECK FOR AVAILABLE NFC ADAPTOR
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter == null) {
mInfoText.setText("NFC is not available on this device.");
} else {
// Register callback to set NDEF message
mNfcAdapter.setNdefPushMessageCallback(this, this);
// Register callback to listen for message-sent success
mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
}
}
createNdefMessage
public NdefMessage createNdefMessage(NfcEvent event) {
Time time = new Time();
time.setToNow();
mInfoText.setTextColor(Color.WHITE);
mInfoText.setText("File Transfer In Progress ...");
NdefMessage msg = new NdefMessage(NdefRecord.createMime(
"application/com.example.android.beam", text.getBytes()));
return msg;
}
My app throughs an exception at
mInfoText.setTextColor(Color.WHITE);
Is it because i am having two intents?
I think that when you call mInfoText.setTextColor(Color.WHITE) mInfoText is null. Try initialising with findViewById() before you call setTextColor

How to make a confirmation after sending sms from emulator to other mobile numbers?

Assume,
i have sent a sms from emulator to my mobile number,but my mobile received nothing.Is it possible to view that sent sms on my mobile from "EMULATOR".If so how?
I have done this so far and its toasting as "SMS sent".
Please find my source below
public class Send_sms extends Activity {
private static final String TAG = "Send_sms";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main12);
Button sndbtn =(Button)findViewById(R.id.but_send_ok);
sndbtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
EditText addrTxt = (EditText)Send_sms.this.findViewById(R.id.editText_number);
EditText msgTxt = (EditText)Send_sms.this.findViewById(R.id.editText_write_msg);
try
{
sendSmsMessage(addrTxt.getText().toString(),msgTxt.getText().toString());
Toast.makeText(Send_sms.this,"Sms sent",Toast.LENGTH_LONG).show();
}
catch(Exception e)
{
Toast.makeText(Send_sms.this,"Failed to send sms",Toast.LENGTH_LONG).show();
}
}});
}
#Override
protected void onDestroy() {
super.onDestroy();
}
private void sendSmsMessage(String address,String message)throws Exception
{
SmsManager smsMgr = SmsManager.getDefault();
smsMgr.sendTextMessage(address,null,message,null,null);
}
}
No it is not possible to send sms for emulator to real device because Emulator has a virtual device and a virtual device not able to communicate with a real device but you are able to send sms from a virtual device to another virtual device.
Using PendingIntent object you're able to receive notifications from the Android system whether SMS was sent successfully or it failed.
To do so the 4th and 5th parameter of sendTextMessage() should be set. See details here: enter link description here
For a working example check this out: http://mobiforge.com/developing/story/sms-messaging-android

Reading text file from SD card

I have an app that send SMS for checking remaining MB in my data package. I have a layout with a button and a text view. When I press my button, I send a message to my phone operator. Then I have a broadcast receiver, that listens to incoming messages, and saves message body to a text file. I Want to show this text in my text view when I get answer from my operator.
This is my code:
public class bonbon3 extends Activity
{
Button btnStanje;
Context context=this;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main3);
btnStanje = (Button) findViewById(R.id.provjeriStanje);
btnStanje.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
String phoneNo = "0977";
String message = "stanje";
sendSMS(phoneNo, message);
Toast.makeText(getBaseContext(), "Zahtjev za provjeru stanja paketa je poslan, odgovor očekuj uskoro!", Toast.LENGTH_SHORT).show();
File root = Environment.getExternalStorageDirectory();
File dir = new File (root.getAbsolutePath() + "/Bonbon info");
dir.mkdirs();
File f = new File(dir, "test.txt");
StringBuilder text = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(f));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append('\n');
}
}
catch (IOException e) {
}
TextView tv = (TextView)findViewById(R.id.textView2);
tv.setText(text);
}
});
}
private void sendSMS(String phoneNumber, String message)
{
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage(phoneNumber, null, message, null, null);
}
}
Now, this code I am trying to read from file, before answer SMS is received, so I know this is wrong, but I don't know how to load text to textView after I get SMS answer?
hi i think this link will be very helpful for you
Reading a text file from sdcard in android is as same as reading a text file in java..
Goran, I cannot reply inline, but if you follow my link, there is pretty much everything you need.
I followed this example almost excatly and it worked out well.
You just need to implement a message handler in your activity (instead of the service as in the example) and push the message from the service (instead of recieving it here) but except that, this is exactely the same.
So in your activity, you should have something like this :
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_GOT_SMS:
// Fill your text view here using the msg.obj (you put it there)
break;
default:
super.handleMessage(msg);
}
}
}
In you service (the bit that recieves the SMS) you should have something like:
public void sendText(String sms) {
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MyActivity.MSG_GOT_SMS,O, 0, sms);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
I think you would rather establish a messaging communication between your activity and your broadcast reciever (that is a service isn't it?).
You are bound to have a loop that will try to read the file continously until it is there whci does not feel right.
Do not store the SMS in a text file send the content directly from the activity to the service using the messaging system.
For messaging between activities and services, have a look in the Android developper guide about Bound services and especially the section about messenger (your service does not need to be bound).

Categories

Resources