How to read data from an NFC device using C# android? - c#

I am trying to read data from an NFC credit card. The app is written using the .NET Maui framework. Right now I am trying to use the Plugin.NFC NuGet package because it seems to be the only one.
Here is the code I have so far:
private TagInfo tmpData;
private static void ReadTagEvent(ITagInfo data)
{
tmpData = (TagInfo)data;
}
public static NFC_READ_ERROR ReadNFC(ref TagInfo result, Page page)
{
if (!IsReady())
{
return NFC_READ_ERROR.NOT_ENABLED;
}
tmpData = null;
CrossNFC.Current.StartListening();
CrossNFC.Current.OnMessageReceived += ReadTagEvent;
page.DisplayAlert("Info", "Starting listening for NFC...", "Ok");
while (tmpData == null)
{
Thread.Sleep(50);
}
page.DisplayAlert("Info", "tmpData != null", "Ok");
CrossNFC.Current.StopListening();
result = tmpData;
if (tmpData.IsEmpty)
{
return NFC_READ_ERROR.NO_DATA;
}
return NFC_READ_ERROR.SUCCESS;
}
This runs and displays the first alert. But the result of tagInfo saved to tmpData is still null. Because ReadTagEvent is never triggered. After the Start listening is called. And the device is in close proximity to the android phone it beeps so I know that it is listening. I have confirmed that the device has data and is working properly by testing it on another app. The second alert is never displayed. By the way, this code is run inside a Thread in the app's Display Page. Any help and code would be appreciated. Thanks.

Add NFC Permissions in your AndroidManifest.xml
&ltuses-permission android:name="android.permission.NFC" />
&ltuses-feature android:name="android.hardware.nfc" android:required="false" />
Add the line CrossNFC.Init(this) in your OnCreate().
protected override void OnCreate(Bundle savedInstanceState)
{
CrossNFC.Init(this);
base.OnCreate(savedInstanceState);
[...]
}
3. Add the line CrossNFC.OnResume() in your OnResume()
// Plugin NFC: Restart NFC listening on resume (needed for Android 10+)
4. Add the line CrossNFC.OnNewIntent(intent) in your OnNewIntent()
// Plugin NFC: Tag Discovery Interception
Plugin.NFC https://github.com/franckbour/Plugin.NFC

Related

how do i get Android.Media.SetPreferredDevice() to work

So making a mobile application that works on UWP, IOS and Android but since not all librarys work on every platform I'm using the library based on what device is used by
if (Device.RuntimePlatform == Device.Android) { }
And I'm currently only working on the Android part of the application.
I'm using Android.Media to play a single audio file out of multiple speakers. And to do that I'm using a Picker that has the available audio output devices. This part works.
But I'm getting a error while trying to select the PreferredDevice:
Java.Lang.NoSuchMethodError: 'no non-static method "Landroid/media/MediaPlayer;.setPreferredDevice(Landroid/media/AudioDeviceInfo;)Z"'
The code line that is giving the error is:
mediaPlayer1.SetPreferredDevice(audioDeviceInfo);
the full method that is being run is:
newoutput.SelectedIndexChanged += (changed, args) =>
{
Context context = Android.App.Application.Context;
AudioManager audioMan = (AudioManager)context.GetSystemService(Context.AudioService);
AudioDeviceInfo audioDeviceInfo = audioMan.GetDevices(GetDevicesTargets.Outputs)[newoutput.SelectedIndex];
mediaPlayer1.SetPreferredDevice(audioDeviceInfo);
};
I can't find many examples that use the method and they don't usually go with a mediaplayer that is created by button press.
You can use this code
private AudioDeviceInfo findAudioDevice(int deviceType) {
AudioManager manager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
AudioDeviceInfo[] adis = manager.getDevices(GET_DEVICES_OUTPUTS);
for (AudioDeviceInfo adi : adis) {
if (adi.getType() == deviceType) {
return adi;
}
}
return null;
}
Then set your input:
audioRecord.setPreferredDevice(findAudioDevice([newoutput.SelectedIndex]));

Find out if a package was installed successfully

I'm trying to add an auto update functionality to a Xamarin Android app.
When it starts, the app checks whether the current version is allowed and if not retrieves all the packages to update (one or more) from the database.
Then it downloads all the packages and saves them to disk. Once all packages have been downloaded, I prompt android to install them in sequence as such:
foreach (UpdatedApp app in apps)
{
InstallPackage(app);
}
private void InstallPackage(UpdatedApp app)
{
Java.IO.File file = new Java.IO.File(app.Path);
if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
{
Intent intent = new Intent(Intent.ActionInstallPackage);
intent.SetData(FileProvider.GetUriForFile(this, $"{PackageName}.fileprovider", file));
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
StartActivity(intent);
}
else
{
Intent intent = new Intent(Intent.ActionView);
intent.SetDataAndType(Android.Net.Uri.FromFile(file), "application/vnd.android.package-archive");
intent.AddFlags(ActivityFlags.NewTask);
StartActivity(intent);
}
It works great when there is only one package to install, but when there are at least two, I basically prompt android to install the packages simultaneously and only the first package will be installed.
I added a small bit of logic to circumvent this issue:
Get the current version of the Nth package.
Prompt install
Every x seconds for y seconds, get the package version again and compare it to the original
If different it means the package has successfully been installed and repeat the process for the N+1st package
Otherwise cancel any further installation
Here's the updated code:
foreach (UpdatedApp app in apps)
{
string versionName = GetPackageVersion(app);
InstallPackage(app);
bool installed = await HasPackageBeenInstalled(app, versionName);
if (!installed)
break;
}
private async Task<bool> HasPackageBeenInstalled(UpdatedApp app, string oldVersion)
{
int count = 0;
do
{
await Task.Delay(1000);
string newVersion = GetPackageVersion(app);
if (newVersion != oldVersion)
return true;
count++;
}
while (count < 20);
return false;
}
private string GetPackageVersion(UpdatedApp app)
{
try
{
PackageInfo packageInfo = PackageManager.GetPackageInfo(app.PackageName, 0);
return packageInfo.VersionName;
}
catch (PackageManager.NameNotFoundException e)
{
return null;
}
}
It ensures I only prompt to install a package after the previous one has been installed.
I was wondering if there was a better way to do that? I had two things in mind:
Use StartActivityForResult instead of StartActivity. This assumes Android natively returns a result, which is not clear to me.
Use a BroadcastReceiver. This assumes Android natively sends a broadcast message when any app is successfully installed.
Could any of my ideas work? Or anything else?
You can use BroadcastReceiver to detect the PACKAGE_ADDED, PACKAGE_REMOVED message::
In your manifest:
<receiver android:name=".AppStatusReceiver" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
</intent-filter>
</receiver>
And Ccreate a Broadcast Receiver:
[BroadcastReceiver(Enabled = true, Exported = false)]
public class SampleReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
// Do stuff here.
}
}
Please Note that the newly installed package does not receive this broadcast.

Problem with Messaging Center when using it in OnCreate (Android) or AppDelagate (iOS) [Xamarin.Forms]

I am using the Messaging Center to send and receive messages/data throughout the app. The app handles custom file extensions and loads them into the app and work on those files.
There are two ways to import files into the app:
1- Pushing the Browse button in the app and choosing the file (This is working fine).
2- Pressing directly on the file, since the file extension is associated with the app, the application opens up and imports the selected file. This is where the problems start.
If the application has not opened before and is not in the background (that means there are no subscribers yet), the application starts and does nothing, as expected. The app sending the selected files to the subscriber but there is no subscriber available yet because the form did not complete opening (pages and view models executed after MainActivity or AppDelagate).
If the app is in the background, clicking the file opens the app and works as expected.
I am aware of the problem but I do not know how to get around it. Any suggestions?
This is MainActivity.cs in Android:
protected override async void OnNewIntent(Intent intent)
{
if (intent?.Data == null)
return;
// Open a stream from the URI
var lasStream = ContentResolver.OpenInputStream(intent.Data);
// Save it over
var memStream = new MemoryStream();
await lasStream.CopyToAsync(memStream);
var byteArray = memStream.ToArray();
MessagingCenter.Send(byteArray, "ByteArrayReceived");
}
This method is called in one of the ViewModels in the app:
private async Task ByteArrayReceived(byte[] byteArray)
{
// Handle incoming file as a byte array
}
UPDATE:
I think the question was not that clear. I will try to explain a little bit more.
What I want to do is that when user selects an external file (this could be a txt file or something else), this app should fire up and load the contents of selected file into the app.
Incoming File is being detected in MainActivity.cs which is executed first when the app is opening, and that means there cannot be any subscribers available yet, because app did not finish loading and the subscriber method has not been called yet. The subscriber method is in MainPageView.cs
I know that subscriber method should be called before sending messages but how do I do it in MainActivity.cs or AppDelagate.cs?
UPDATE 2:
MainPageView is called after MainActivity.cs is executed.
This is MainActivity.cs
protected override void OnCreate(Bundle savedInstanceState)
{
base.Window.RequestFeature(WindowFeatures.ActionBar);
base.SetTheme(Resource.Style.MainTheme);
if (Intent.Action == Intent.ActionSend || Intent.Action == Intent.ActionView)
HandleIntent(Intent);
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
This is App.cs
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPageView());
}
One solution I find is that you can send the message after X seconds by using Device.StartTimer:
protected override async void OnNewIntent(Intent intent)
{
if (intent?.Data == null)
return;
// Open a stream from the URI
var lasStream = ContentResolver.OpenInputStream(intent.Data);
// Save it over
var memStream = new MemoryStream();
await lasStream.CopyToAsync(memStream);
var byteArray = memStream.ToArray();
// run task after 2 second
Device.StartTimer(TimeSpan.FromSeconds(2), () =>
{
MessagingCenter.Send(byteArray, "ByteArrayReceived");
// No longer need to recur. Stops firing task
return false;
});
}

Xamarin Android: how to share a string via text/email/Whatsapp/Facebook/WeChat

My Xamarin app for Android reads some local data, process them and returns the results as a string like:
"Current target is X, please increase power to Y".
I want to have a button in my app that will open the usual sshare command that we see in many applications and where it is possible to say that the content will be shared with the apps that you have on your phone.
For example if I use WeChat or even WhatsApp I can click on that icon and select the contact to share my string.
How can I achieve this in Xamarin. In particular, I would need to interact with WeChat, WhatsApp and Facebook.
I want to have a button in my app that will open the usual sshare command that we see in many applications and where it is possible to say that the content will be shared with the apps that you have on your phone.
You can refer to the official doc: Sending Simple Data to Other Apps for Android, in Xamarin.Android, it's similar, for example:
Intent intentsend = new Intent();
intentsend.SetAction(Intent.ActionSend);
intentsend.PutExtra(Intent.ExtraText, "Current target is X, please increase power to Y");
intentsend.SetType("text/plain");
StartActivity(intentsend);
I personally have not tried that as I do not need it this far. However, I found this Xamarin.Social package and probably sharing to you is helpful. Here is the example at their GitHub repo. I will attach the code snippet from their example so it is easier to you to read here.
//taken from https://github.com/xamarin/Xamarin.Social/blob/master/samples/Xamarin.Social.Sample.Android/MainActivity.cs
void Share (Service service, Button shareButton)
{
Item item = new Item {
Text = "I'm sharing great things using Xamarin!",
Links = new List<Uri> {
new Uri ("http://xamarin.com"),
},
};
Intent intent = service.GetShareUI (this, item, shareResult => {
shareButton.Text = service.Title + " shared: " + shareResult;
});
StartActivity (intent);
}
public override void OnCreate(Bundle bundle)
{
...
facebookButton.Click += (sender, args) =>
{
try
{
Share (Facebook, facebookButton);
}
catch (Exception ex)
{
ShowMessage("Facebook: " + ex.Message);
}
};
...
}
Hope this could help.

Xamarin Android App not streaming online radio stream

So I'm trying to make a Android app for an online radio and no matter how much I use the guides on Xamarin, it's not streaming.
My code: http://pastebin.com/K20RX8Yk
The coding environment I am using is Xamarin.Android and I am coding in the Xamarin Studio. I've been using the Android emu running API 16.
I have already tried different path files but sadly that hasn't fixed it.
I would advice to use the MediaPlayer to stream audio on Android, as it is much easier to use then AudioTrack. Potentially AudioTrack could have better performance but you need to do quite some stuff yourself to get it working correctly.
Here is a blog post with example how to use MediaPlayer on Xamarin Android: http://blog.xamarin.com/background-audio-streaming-with-xamarin.android/
A quick example out of that is:
private const string Mp3 = #"http://167.88.113.131:8000/;stream.mp3";
private MediaPlayer player;
private void IntializePlayer()
{
player = new MediaPlayer();
//Tell our player to sream music
player.SetAudioStreamType(Stream.Music);
}
private async void Play()
{
if (player == null) {
IntializePlayer();
}
try {
await player.SetDataSourceAsync(ApplicationContext, Android.Net.Uri.Parse(Mp3));
player.PrepareAsync();
player.Start();
}
catch (Exception ex) {
//unable to start playback log error
Console.WriteLine("Unable to start playback: " + ex);
}
}

Categories

Resources