I'm new to Xamarin cross-platform app development, and I'm trying to implement on Android version of my app an external NFC tag reading.
When a tag is scanned, I would like my application to open and read the text inside the tag and finally perform some specific actions based on what was read.
I've this implementation on MainActivity.cs, but it doesn't work because it seems that I can't get the intent:
using Android.Content;
using System;
using System.Text;
using System.Diagnostics;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Xamarin.Forms;
using Android.Content.Res;
using FFImageLoading.Forms.Platform;
using Plugin.Permissions;
using Plugin.Permissions.Abstractions;
using Plugin.CurrentActivity;
using Android.Nfc;
using Android.Nfc.Tech;
using Poz1.NFCForms.Abstract;
using Poz1.NFCForms.Droid;
namespace Kibelis.Droid
{
[Activity(Label = "Kibelis", Icon = "#drawable/icon", Theme = "#style/MainTheme", LaunchMode = LaunchMode.SingleTop, MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
private NfcAdapter _nfcAdapter;
public object NFCUtil { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
CachedImageRenderer.Init(true);
base.OnCreate(savedInstanceState);
CrossCurrentActivity.Current.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
_nfcAdapter = NfcAdapter.GetDefaultAdapter(this);
System.Diagnostics.Debug.WriteLine("CREATE");
// is attached.
LoadApplication(new App());
}
protected override void OnResume()
{
base.OnResume();
if (_nfcAdapter == null)
{
System.Diagnostics.Debug.WriteLine("NFC UNAVIABLE");
}
else
{
var tagDetected = new IntentFilter(NfcAdapter.ActionNdefDiscovered);
var filters = new[] { tagDetected };
var intent = new Intent(this, this.GetType()).AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, 0);
_nfcAdapter.EnableForegroundDispatch(this, pendingIntent, filters, null);
System.Diagnostics.Debug.WriteLine("FOREGRAUND DISPATCH");
}
}
protected override void OnPause()
{
base.OnPause();
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
System.Diagnostics.Debug.WriteLine("NEW INTENT");
if (intent.Extras.IsEmpty)
{
System.Diagnostics.Debug.WriteLine("empty");
}
else
{
System.Diagnostics.Debug.WriteLine("Not empty");
}
//For start reading
if (intent.Action == NfcAdapter.ActionTagDiscovered || intent.Action == NfcAdapter.ActionNdefDiscovered || intent.Action == NfcAdapter.ActionAdapterStateChanged
|| intent.Action == NfcAdapter.ActionTransactionDetected || intent.Action == NfcAdapter.ExtraNdefMessages || intent.Action == NfcAdapter.ExtraNdefMessages)
{
System.Diagnostics.Debug.WriteLine("DISCOVERD");
var tag = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag;
if (tag != null)
{
System.Diagnostics.Debug.WriteLine("TAG");
// First get all the NdefMessage
var rawMessages = intent.GetParcelableArrayExtra(NfcAdapter.ExtraNdefMessages);
if (rawMessages != null)
{
var msg = (NdefMessage)rawMessages[0];
System.Diagnostics.Debug.WriteLine("MESSAGE");
// Get NdefRecord which contains the actual data
var record = msg.GetRecords()[0];
if (record != null)
{
if (record.Tnf == NdefRecord.TnfWellKnown)
{
// Get the transfered data
var data = Encoding.ASCII.GetString(record.GetPayload());
System.Diagnostics.Debug.WriteLine("RECORD");
}
}
}
}
}
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
Can you help me?
You are registering the foreground dispatch for the ActionNdefDiscovered intent. However, this intent filter also requires a specific data type (to be present on the tag and to be registered for the intent). If that's what you want, you need to add that data type (MIME type or URI) to the intent filter (variable tagDetected).
If, instead, you want to just listen for all tags, you want to use the ActionTagDiscovered intent instead. In fact, you can simply skip the intnent filter all together from the call to EnableForegroundDispatch:
_nfcAdapter.EnableForegroundDispatch(this, pendingIntent, null, null);
Related
I'm trying to develop a mobile app in Xamarin Forms...One of the functionalities is text recognition. I need to recognize the text and then send it to another page (xaml). So i made an activity where it does recognize the text but i don't know how to return the string "Resultados" (Results). I tried to do a little search and many suggested that the "OnActivityResult" should return the values...but it doesnt get triggered.
Can anyone help me out what i am doing wrong?
TextRecognition.cs - activity(android)
public class TextRecognition : AppCompatActivity, ISurfaceHolderCallback, IProcessor, ITextRecognition
{
private SurfaceView cameraView;
private TextView textView;
private CameraSource cameraSource;
public string Resultados;
private const int RequestCameraPermissionID = 1001;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
cameraView = FindViewById<SurfaceView>(Resource.Id.surface_view);
textView = FindViewById<TextView>(Resource.Id.txtview);
TextRecognizer textRecognizer = new TextRecognizer.Builder(ApplicationContext).Build();
if (!textRecognizer.IsOperational)
{
Log.Error("Main Activity", "Detector dependencies are not yet available");
}
else
{
cameraSource = new CameraSource.Builder(ApplicationContext, textRecognizer)
.SetFacing(CameraFacing.Back)
.SetRequestedFps(2.0f)
.SetRequestedPreviewSize(1280, 1024)
.SetAutoFocusEnabled(true)
.Build();
cameraView.Holder.AddCallback(this);
textRecognizer.SetProcessor(this);
}
Android.Widget.Button logonButton = FindViewById<Android.Widget.Button>(Resource.Id.button_send);
logonButton.Click += delegate {
button_OnClick();
};
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
switch (requestCode)
{
case RequestCameraPermissionID:
{
if (grantResults[0] == Android.Content.PM.Permission.Granted)
{
cameraSource.Start(cameraView.Holder);
}
}
break;
}
}
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
{
}
public void SurfaceCreated(ISurfaceHolder holder)
{
if (ActivityCompat.CheckSelfPermission(ApplicationContext, Manifest.Permission.Camera) != Android.Content.PM.Permission.Granted)
{
ActivityCompat.RequestPermissions(this, new string[]
{
Android.Manifest.Permission.Camera
}, RequestCameraPermissionID);
return;
}
cameraSource.Start(cameraView.Holder);
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
cameraSource.Stop();
}
public void ReceiveDetections(Detections detections)
{
SparseArray items = detections.DetectedItems;
if (items.Size() != 0)
{
textView.Post(() =>
{
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < items.Size(); i++)
{
strBuilder.Append(((TextBlock)items.ValueAt(i)).Value);
strBuilder.Append("\n");
}
textView.Text = strBuilder.ToString();
Resultados = strBuilder.ToString();
});
}
}
async void button_OnClick()
{
Toast.MakeText(this, "Hello from " + Resultados, ToastLength.Long).Show();
Intent data = new Intent(this, typeof(TextRecognition));
SetResult(Result.Ok, data);
Finish();
}
public void Release()
{
}
public string LaunchActivityInAndroid()
{
Activity activity = Forms.Context as Activity;
var intent = new Intent(Forms.Context, typeof(TextRecognition));
activity.StartActivityForResult(intent, Convert.ToInt32(Result.Ok));
return Resultados;
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
switch(resultCode)
{
case Result.Ok:
break;
}
Finish();
}
public interface ITextRecognition
{
}
}
I tried to do a little search and many suggested that the "OnActivityResult" should return the values...but it doesnt get triggered.
You are using OnActivityResult a bit wrong. For example, if you have two Activity, ActivityA and ActivityB, OnActivityResult method in ActivityA, ActivityB need return some value to ActivityA, we can use activityA.StartActivityForResult() to open ActivityB,
ActivityB Use SetResult method to send data to ActivityA.
public class ActivityB: Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
Intent intent = new Intent();
intent.PutExtra("respond", "Hello,Alice!I'm Bob.");
SetResult(Result.Ok, intent);
}
}
Then OnActivityResultwill be triggered in the ActivityA.
I need to recognize the text and then send it to another page (xaml)
If you want to send data from Activity to Forms pages, You can use MessagingCenter to achieve it.
In Activity, we can use following code to send it.
MessagingCenter.Send<App, string>(App.Current as App, "OpenPage", "You send message:" + Resultados);
In the xamarin forms pages, you can use following code to get it.
MessagingCenter.Subscribe<App, string>(App.Current, "OpenPage", (snd, arg) =>
{
var getValue = arg;
});
Here is a link about MessagingCenter.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/messaging-center
===========update===========
I write a scan demo. Here is running gif.
Here is interface about dependenceService.
public interface ITextRecognition
{
void LaunchActivityInAndroid();
}
Here is achievement about dependenceService.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Views;
using Android.Widget;
using FormsTextRecognizer.Droid;
using Xamarin.Forms;
using static Android.Gms.Vision.Detector;
using Android.Gms.Vision;
using Android.Gms.Vision.Texts;
using Android.Support.V4.App;
using Android;
using Android.Content.PM;
using Android.Util;
[assembly: Dependency(typeof(TextRecognition))]
namespace FormsTextRecognizer.Droid
{
[Activity(Label = "ScanActivity", Theme = "#style/Theme.AppCompat.Light.NoActionBar")]
public class TextRecognition : AppCompatActivity, ITextRecognition, ISurfaceHolderCallback, IProcessor
{
// private CameraSource cameraSource;
private SurfaceView cameraView;
TextRecognizer textRecognizer;
private const int RequestCameraPermissionID = 1001;
private CameraSource cameraSource;
public string Resultados;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
cameraView = FindViewById<SurfaceView>(Resource.Id.surface_view);
Android.Widget.Button logonButton = FindViewById<Android.Widget.Button>(Resource.Id.button_send);
textRecognizer = new TextRecognizer.Builder(Android.App.Application.Context).Build();
// Android.Gms.Vision.Frame frame = new Android.Gms.Vision.Frame.Builder().SetBitmap(bitmap).Build();
if (!textRecognizer.IsOperational)
{
Log.Error("Main Activity", "Detector dependancies are not yet available");
}
else
{
cameraSource = new CameraSource.Builder(ApplicationContext, textRecognizer)
.SetFacing(CameraFacing.Back)
.SetRequestedFps(2.0f)
.SetRequestedPreviewSize(1920, 1080)
.SetAutoFocusEnabled(true)
.Build();
cameraView.Holder.AddCallback(this);
textRecognizer.SetProcessor(this);
}
logonButton.Click += LogonButton_Click;
}
private void LogonButton_Click(object sender, EventArgs e)
{
// throw new NotImplementedException();
Toast.MakeText(this, Resultados, ToastLength.Short).Show();
MessagingCenter.Send<App, string>(App.Current as App, "OpenPage", "You send message:" + Resultados);
Finish();
}
public void LaunchActivityInAndroid()
{
//string ScanText = "";
Activity activity = Forms.Context as Activity;
var intent = new Intent(Forms.Context, typeof(TextRecognition));
activity.StartActivity(intent);
// activity.StartActivityForResult(intent, Convert.ToInt32(Result.Ok));
// return Resultados;
}
public void ReceiveDetections(Detections detections)
{
//throw new NotImplementedException();
SparseArray items = detections.DetectedItems;
if (items.Size() != 0)
{
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < items.Size(); i++)
{
strBuilder.Append(((TextBlock)items.ValueAt(i)).Value);
strBuilder.Append("\n");
}
// textView.Text = strBuilder.ToString();
Resultados = strBuilder.ToString();
// });
}
}
public void Release()
{
// throw new NotImplementedException();
}
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
{
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
switch (requestCode)
{
case RequestCameraPermissionID:
{
if (grantResults[0] == Permission.Granted)
{
cameraSource.Start(cameraView.Holder);
}
}
break;
}
}
public void SurfaceCreated(ISurfaceHolder holder)
{
//throw new NotImplementedException();
if (ActivityCompat.CheckSelfPermission(ApplicationContext, Manifest.Permission.Camera) != Android.Content.PM.Permission.Granted)
{
//Request Permission
ActivityCompat.RequestPermissions(this, new string[] {
Android.Manifest.Permission.Camera
}, RequestCameraPermissionID);
return;
}
cameraSource.Start(cameraView.Holder);
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
cameraSource.Stop();
}
}
}
Here is Forms page background code.
[DesignTimeVisible(false)]
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
MessagingCenter.Subscribe<App, string>(App.Current, "OpenPage", (snd, arg) =>
{
scanLabel.Text = arg;
});
}
private void Button_Clicked(object sender, EventArgs e)
{
DependencyService.Get<ITextRecognition>().LaunchActivityInAndroid();
}
}
Here is my demo, you can refer to it.
https://github.com/851265601/FormsTextRecognizer
I'm trying to read a mifare card. I can read the Tag object but I can't read the NdefMessages ... When I call the intent to retrieve the ndefMessages it returns a null value instead of an array. However I can read the ID and the techlist and when I read it with another app I can see the contents so the card has data.
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Nfc;
using Android.Nfc.Tech;
using Android.OS;
using Android.Runtime;
using System;
using System.Linq;
using System.Text;
namespace NfcDemo.Droid
{
[Activity(Label = "NfcDemo", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, LaunchMode = LaunchMode.SingleTop | LaunchMode.SingleTask)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
private NfcAdapter _nfcAdapter;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
_nfcAdapter = NfcAdapter.GetDefaultAdapter(this);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected override void OnResume()
{
base.OnResume();
if (_nfcAdapter == null)
{
var alert = new AlertDialog.Builder(this).Create();
alert.SetMessage("NFC is not supported on this device.");
alert.SetTitle("NFC Unavailable");
alert.Show();
}
else
{
var ndefDiscover = new IntentFilter(NfcAdapter.ActionNdefDiscovered);
var techDiscover = new IntentFilter(NfcAdapter.ActionTechDiscovered);
var tagDetected = new IntentFilter(NfcAdapter.ActionTagDiscovered);
var filters = new[] { tagDetected, techDiscover, ndefDiscover };
var intent = new Intent(this, this.GetType()).AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, 0);
_nfcAdapter.EnableForegroundDispatch(this, pendingIntent, filters, null);
}
}
protected override void OnPause()
{
base.OnPause();
_nfcAdapter.DisableForegroundDispatch(this);
}
public static string ByteArrayToString(byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
if (intent.Action == NfcAdapter.ActionTagDiscovered)
{
var tag = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag;
if (tag != null)
{
var techs = tag.GetTechList();
var id = tag.GetId();
var hexstring = ByteArrayToString(tag.GetId());
var rawMessages = intent.GetParcelableArrayExtra(NfcAdapter.ExtraNdefMessages);
if (rawMessages != null)
{
foreach (var message in rawMessages.Cast<NdefMessage>())
{
foreach (var record in message.GetRecords())
{
var typeBytes = record.GetTypeInfo();
var payloadBytes = record.GetPayload();
var type = Encoding.UTF8.GetString(typeBytes);
var payload = Encoding.UTF8.GetString(payloadBytes);
}
}
}
}
}
}
}
}
You should be looking for NfcAdapter.ACTION_NDEF_DISCOVERED not NfcAdapter.ActionTagDiscovered in you onNewIntent
but as you have not correctly asked to be sent NDEF messages in your intent filter, you won't get any, you should add ndefDiscover.addDataType("*/*"); to tell it what type of NDEF messages your want before you pass the filter to EnableForegroundDispatch
Note "*/*" is to send all types, having no data type defined is to send zero data types.
Because TagDiscovered is a lower type than NDEF, even if you card has NDEF messages on it the background service won't parse them to put them in the Extras field as it is not expecting you to use them.
Docs on this are at https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch
Mifare Classic not work with NDEF, try somethink like this:
using Android.Nfc;
using Android.Nfc.Tech;
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
Tag tag = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag;
if (tag == null) return;
MifareClassic card = MifareClassic.Get(tag);
card.Connect();
//read block 1 of sector 0
if (card.AuthenticateSectorWithKeyA(0, new byte[] {0xff,0xff,0xff,0xff,0xff,0xff})) //replace if you have other auth keys
{
byte[] data = card.ReadBlock(1);
}
card.Close();
}
I'm developing an notepad app, and i need to reload the notes on the view when the Note Editor is closed (i'm using Finish() to close the Editor), but I am not sure how to wait until the Activity closes and does something.
I'm starting the Editor Activity with this:
public async void noteClicked(object sender, EventArgs e)
{
var obj = (RelativeLayout)sender;
var id = obj.Id;
var note = Util.db.Query<Note>(string.Format("SELECT * FROM Notes WHERE ID={0}", id));
var intent = new Intent(this, typeof(EditorAcitivity));
intent.PutExtra("Note", JsonConvert.SerializeObject(note));
intent.PutExtra("Mode", "Edit");
StartActivity(intent);
}
On the EditorActivity.cs:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.editor_activity);
Util.context = this;
mode = Intent.GetStringExtra("Mode");
txtTitle = FindViewById<EditText>(Resource.Id.editorTitle);
txtText = FindViewById<EditText>(Resource.Id.editorText);
if (mode == "Edit")
{
note = JsonConvert.DeserializeObject<Note>(Intent.GetStringExtra("Note"));
txtTitle.Text = note.title;
txtText.Text = note.text;
}
FindViewById<ImageButton>(Resource.Id.editorDelete).Click += editorDelete_Clicked;
FindViewById<ImageButton>(Resource.Id.editorSave).Click += editorSave_Clicked;
}
private void editorSave_Clicked(object sender, EventArgs e)
{
if (txtText.Text.Length == 0 || string.IsNullOrWhiteSpace(txtText.Text))
{
Util.makeShortText("Null text");
}
else
{
var note = new Note();
note.date = Util.now();
note.text = txtText.Text;
if (string.IsNullOrWhiteSpace(txtTitle.Text))
{
note.title = " ";
}
else {
note.title = txtTitle.Text;
}
Util.db.Insert(note);
this.Finish();
}
}
I want to do something like loadNotes() when the Activity is finished (this.Finish())
Edit: I don't wanna return some data, just wait the activity.
I would suggest you to use StartActivityForResult
var intent = new Intent(this, typeof(EditorAcitivity));
intent.PutExtra("Note", JsonConvert.SerializeObject(note));
intent.PutExtra("Mode", "Edit");
StartActivityForResult(intent,REQUEST_CODE_EDITOR_ACITIVTY);
In that case REQUEST_CODE_EDITOR_ACITIVTY is a integer constant.
And then when your about to finish your activity call to the SetResult method as below
Util.db.Insert(note);
SetResult(Result.Ok);
this.Finish();
And finally override the OnActivityResult method in the same activity you're starting the EditorAcitivity as below
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_CODE_EDITOR_ACITIVTY) {
if(resultCode == Result.Ok) {
//Here your activity received the callback and you can call the load notes method
loadNotes();
}
}
}
Take a look at this tutorial if you want to learn more about Activity for result
https://subscription.packtpub.com/book/application_development/9781784398576/8/ch08lvl1sec84/obtaining-data-from-activities
I recently just started programming and developing app. I would appreciate if anybody can help me. I had did the QR scanner with ZXing and are able to display the result but i would like it to open the browser automatically if an URL is detected from the QR code
The following is my code for my Scanner Activity:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.Support.V7.App;
using Android.Views;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Widget;
using Android.Gms.Vision.Barcodes;
using Android.Gms.Vision;
using Android.Graphics;
using Android.Content.PM;
using Android.Support.V4.App;
using Android;
using static Android.Gms.Vision.Detector;
using Android.Util;
namespace com.xamarin.sample.splashscreen
{
[Activity(Label = "Scanner")]
public class Scanner : AppCompatActivity,ISurfaceHolderCallback, IProcessor
{
SurfaceView cameraPreview;
TextView txtResult;
BarcodeDetector barcodeDetector;
CameraSource cameraSource;
const int RequestCameraPermissionID = 1001;
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
switch(requestCode)
{
case RequestCameraPermissionID:
{
if (grantResults[0]==Permission.Granted)
{
if (ActivityCompat.CheckSelfPermission(ApplicationContext, Manifest.Permission.Camera) != Android.Content.PM.Permission.Granted)
{
ActivityCompat.RequestPermissions(this, new string[]
{
Manifest.Permission.Camera
}, RequestCameraPermissionID);
return;
}
try
{
cameraSource.Start(cameraPreview.Holder);
}
catch (InvalidOperationException)
{
}
}
}
break;
}
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Scanner);
cameraPreview = FindViewById<SurfaceView>(Resource.Id.cameraPreview);
txtResult = FindViewById<TextView>(Resource.Id.txtResult);
barcodeDetector = new BarcodeDetector.Builder(this)
.SetBarcodeFormats(BarcodeFormat.QrCode).Build();
cameraSource = new CameraSource.Builder(this, barcodeDetector)
.SetRequestedPreviewSize(640, 480).Build();
cameraPreview.Holder.AddCallback(this);
barcodeDetector.SetProcessor(this);
}
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
{
}
public void SurfaceCreated(ISurfaceHolder holder)
{
if (ActivityCompat.CheckSelfPermission(ApplicationContext, Manifest.Permission.Camera) != Android.Content.PM.Permission.Granted)
{
ActivityCompat.RequestPermissions(this, new string[]
{
Manifest.Permission.Camera
}, RequestCameraPermissionID);
return;
}
try
{
cameraSource.Start(cameraPreview.Holder);
}
catch (InvalidOperationException)
{
}
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
cameraSource.Stop();
}
public void ReceiveDetections(Detections detections)
{
SparseArray qrcodes = detections.DetectedItems;
if(qrcodes.Size()!=0)
{
txtResult.Post(() =>
{
Vibrator vib = (Vibrator)GetSystemService(Context.VibratorService);
vib.Vibrate(1000);
txtResult.Text = ((Barcode)qrcodes.ValueAt(0)).RawValue;
});
}
}
public void Release()
{
}
}`enter code here`
}`enter code here`
I would like it to open the browser automatically if an URL is detected from the QR code
When you get the ZXing.Result from the QR code, you could use Patterns.WebUrl.Matcher method to confirm whether this is a URL.
When you use Patterns.WebUrl.Matcher(result.Text).Matches() method, this regular expression pattern to match most part of RFC 3987 Internationalized URLs, aka IRIs.
Code like this :
private void HandleScanResult(ZXing.Result result)
{
string msg = "";
if (result != null && !string.IsNullOrEmpty(result.Text))
msg = "Found Barcode: " + result.Text;
else
msg = "Scanning Canceled!";
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.PutString("result", result.Text);
resultIntent.PutExtras(bundle);
this.SetResult(Result.Ok, resultIntent);
if (Patterns.WebUrl.Matcher(result.Text).Matches())
{
//If this result get from the QR code is an URL, open the browse.
Intent intent = new Intent();
intent.SetAction("android.intent.action.VIEW");
Android.Net.Uri content_url = Android.Net.Uri.Parse(result.Text);
intent.SetData(content_url);
StartActivity(intent);
}
else
{
this.RunOnUiThread(() => Toast.MakeText(this, msg, ToastLength.Short).Show());
}
}
I would like to Start and Handle ZXing intent in non-Activity class. But I don't know how to catch Result in this class.
This code is typed in Xamarin, but it is not important.
ScannerZXing.cs:
Intent intenScanner;
public ScannerZXing()
{
intentScanner = ScanIntent.GetZXingScanIntent();
}
public override void Scan(Activity current)
{
//this.StartActivityForResult(this.intentScanner, 0); // error: Java.Lang.NullPointerExceptionn
//current.StartActivityForResult(this.intentScanner, 0); // OK, but I have to handle result in MainActivity (I don't want it.)
}
protected override void OnActivityResult(int requestCode, [Android.Runtime.GeneratedEnumAttribute()] Android.App.Result resultCode, Android.Content.Intent intent)
{
base.OnActivityResult(requestCode, resultCode, intent);
//....
}
MainClass.cs:
protected override void OnCreate(Bundle bundle)
{
//....
var scanner = new ScannerZXing();
Button btnScan = this.FindViewById<Button>(Resource.Id.btnScan);
btnScan.Click += delegate
{
scanner.Scan(this);
}
//....
}
If I do OnActivityResult in MainActivity, it works. But I want to move Scanning logic to separated class.
You could just get the nuget for zxing.
https://www.nuget.org/packages/ZXing.Net.Mobile
private async Task<string> Scan()
{
var scanner = new ZXing.Mobile.MobileBarcodeScanner() { BottomText = "Place red line on QR image to scan", TopText = "Place red line on QR image to scan", CancelButtonText = "Cancel scan", CameraUnsupportedMessage = "Camera not availabe on device" };
var scanResults = await scanner.Scan();
return scanresults;
}