Simultaneous scanning of barcodes and text(OCR) in xamarin - xamarin.forms

I have a scanner app where we scan the barcodes and Text on the same camera. I have done this in xamarin with the help of Xamarin.Firebase.IOS.MlKit.
In this when the camera is open, we do continous capturing of image frames, and send the captured image for barcode detection and OCR recognition seperately.
public override void DidOutputSampleBuffer(AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
{
lastRunTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
if (lastRunTime - lastAnalysisTime > scanIntervalInMs)
{
lastAnalysisTime = lastRunTime;
try
{
var image = GetImageFromSampleBuffer(sampleBuffer);
if(image == null) return;
var visionImage = new VisionImage(image) {Metadata = metadata };
releaseSampleBuffer(sampleBuffer);
if(isScanningBarcode)
{
DetectBarcodeActionAsync(visionImage);
isScanningBarcode = false;
}
else
{
isScanningBarcode = true;
DetectTextActionAsync(visionImage);
}
}
catch (Exception exception)
{
System.Diagnostics.Debug.WriteLine(exception.Message);
}
}
releaseSampleBuffer(sampleBuffer);
}
//Barcode detection
private async void DetectBarcodeActionAsync(VisionImage image)
{
VisionBarcode[] barcodes = await barcodeDetector.DetectAsync(image);
}
//OCR
private async void DetectTextActionAsync(VisionImage image)
{
VisionText txt = await textRecognizer.ProcessImageAsync(image);
}
This runs properly. But our app is used in package scanning to scan address labels (names of the recipient) and barcodes. So, when the barcodes are damaged or curved/skewed, this MLKit barcode fails to scan such barcodes, and this is quite frustrating for the clients.
Is there a way we can improve upon the MLKIt barcode scanner to scan such damaged barcodes.
I tried the same with some premium scanners like ScanBot and SCandit demo apps, which were able to scan the damaged barcodes quite easily. I had a talk with ScanBot customer service, but they do not provide SIMULTANEOUS barcode and OCR scanning. I am also looking in this ScanDit SDK to accomplish the same.
Can anyone suggest some premium scanners for xamarin app which scans barcodes and OCR in one go. OR any improvements which can be made to existing MLKit scanner so that it can scan damaged/skewed barcodes.

Related

Android BLE scan/connect/notify

I want to developing an app to communicate with a BLE device.
I have NO experience in developing any sort of Bluetooth application using Android.
if any example source code for app can scan-connect and read some characteristic.
its hard to get a working source code
I'd suggest using Kotlin, you can do exactly what you ask for in Kotlin.
Here's an example of scanning for Bluetooth devices:
fun scanForBluetoothDevices() {
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if (bluetoothAdapter == null) {
// Device doesn't support Bluetooth
}
if (!bluetoothAdapter.isEnabled) {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
}
val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter.bondedDevices
// If there are paired devices
if (pairedDevices?.isNotEmpty() == true) {
// Loop through paired devices
pairedDevices.forEach { device ->
// Add the name and address to an array adapter to show in a ListView
Log.d("Bluetooth", "Device: ${device.name}, ${device.address}")
}
}
}

Flutter Firestore take long retrieving data while offline

I am using Firestore in flutter application. Each time user launch the application it retrieves some data from Firestore Cloud.
QuerySnapshot dataSnapshot = await Firestore.instance
.collection('/data')
.getDocuments();
When user opens the application on first time, it required from him to connect online, to get the data, and as Firebase documents say
For Android and iOS, offline persistence is enabled by default. To disable persistence, set the PersistenceEnabled option to false.
So, it should save the data that application have been read before to retrieve it while the device is offline; so user can access application at anytime with the same data that have been read.
The problem is: it takes too long time to retrieve the data while the device is offline, with the same codes and nothing changed!.
I tried to configure how much time it takes? On offline, it takes about 8 minutes and 40 seconds. But while on online, it takes just 10 seconds, maybe less.
So how can I solve this problem?
============
UPDATE
I manged to get more logs about this problem, which after take a lot of time, and will start application with the offline saved data, it prints this log
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
And then take 3 second for example (not much time) and continue with the next works.
I did open a new issue in GitHub too.
Is there a way to limit the time it takes?
And finally, with the help of diegoveloper comment in GitHub issue, I have reached the solution.
This comment
await Firestore.instance
.collection("Collection")
.getDocuments(source: source)
was a good solution if I decided to check source each time and then use it or I can use it in starting of a new Flutter project, but now I already have a lot of codes that need a better solution. So I decided to fork the cloud_firestore package and edit it.
You can find it here: https://github.com/ShadyBoshra2012/flutterfire/tree/master/packages/cloud_firestore
What I have edited:
firestore.dart
// The source of which the data will come from.
static Source _source = Source.serverAndCache;
static Source get source => _source;
Future<void> settings(
{bool persistenceEnabled,
String host,
bool sslEnabled,
bool timestampsInSnapshotsEnabled,
int cacheSizeBytes,
Source source}) async {
await channel.invokeMethod<void>('Firestore#settings', <String, dynamic>{
'app': app.name,
'persistenceEnabled': persistenceEnabled,
'host': host,
'sslEnabled': sslEnabled,
'timestampsInSnapshotsEnabled': timestampsInSnapshotsEnabled,
'cacheSizeBytes': cacheSizeBytes,
});
if (source != null) _source = source;
}
query.dart
source = Firestore.source; Line 92
document_reference.dart
source = Firestore.source; Line 83
How you can use it?
So you can use my forked repository in this way with using connectivity package from Google : https://pub.dev/packages/connectivity .
Add my forked repository in pubspec.yaml file
cloud_firestore:
git:
url: https://github.com/ShadyBoshra2012/flutterfire.git
path: packages/cloud_firestore
Then in your first screen or main
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none) {
await Firestore.instance.settings(source: Source.cache);
} else {
await Firestore.instance.settings(source: Source.serverAndCache);
}
and if you want to refresh the source when change the connection state:
StreamSubscription subscription;
void initState() {
super.initState();
// Check the internet connection after each change
// of the connection.
subscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
// Check the internet connection and then choose the appropriate
// source for it.
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none) {
await Firestore.instance.settings(source: Source.cache);
} else {
await Firestore.instance.settings(source: Source.serverAndCache);
}
});
}
#override
void dispose() {
super.dispose();
subscription.cancel();
}
So I hope it works with everyone see it, and waiting for Flutter Team to code a better and better solution. Thanks for everyone has participated.
In addition to Shady Boshra's answer you can use FirebaseFirestore.instance.disableNetwork() functionality so your code will look like this:
StreamSubscription subscription;
void initState() {
super.initState();
// Check the internet connection after each change
// of the connection.
subscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
// Check the internet connection and then choose the appropriate
// source for it.
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none) {
await FirebaseFirestore.instance.disableNetwork();
} else {
await FirebaseFirestore.instance.enableNetwork();
}
});
}

Bi-Directional Communication via IoTHub/Xamarin App/ESP8266

Working on a new product at work that will be using an ESP8266, Xamarin app, and the Azure IoTHub to enable bidirectional communication for customer's devices.
We've got C2D (Cloud 2 Device) and D2C (Device 2 Cloud) communication working properly on both the app and the ESP, but we are not finding any information on setting up the IoTHub to interpret incoming Telemetry messages, process their respective "To:" field and put them back in to the C2D topic, which should allow our target device to receive it.
What we have tried:
Logic Apps. Were able to trigger on incoming messages to the queue, but not sure what HTTP request to do in order to forward it back in to the C2D event hub.
We have successfully been able to forward each message in to a queue, but the PCL library for Xamarin is not capable of connecting to Azure Service Bus Queues (bummer).
I found a reference for an intern at Microsoft developing direct device to device communication for a garage door opener, but the library she is using is only available for UWP apps, which isn't all that convenient, when we really want to target iOS, Android and UWP (reason for choosing Xamarin in the first place).
https://blogs.windows.com/buildingapps/2016/09/08/device-to-device-communication-with-azure-iot-hub/#ykPJrVE734GpSEzV.97
Has anyone been able to trigger C2D conditional events using the Azure portal?
Through some conversations with Microsoft Azure team, we determined that a webjob combined with a route to a queue was the best solution for us.
All messages are routed to the queue and as they arrive in the queue, the webjob processes the message and sends the message on using a ServiceBus Messaging object to send the cloud to device response message.
Here's the code for anyone who wants to use it.
As long as the original sender of the message specifies the "To" property in the brokered message, it will be delivered to that device in the registry. You will need the Service Bus and Azure.Messaging NuGet packages in order to use this. This code will copy the entire message and send the whole thing to the desired registry device.
private const string queueName = "<queue_name>";
private const string IoTHubConnectionString = "HostName=<your_host>;SharedAccessKeyName=<your_service_user>;SharedAccessKey=<your sas>";
// This function will get triggered/executed when a new message is written
// on an Azure Queue called <queue_name>.
public static void ReceiveQueueMessages(
[ServiceBusTrigger(queueName)] BrokeredMessage message,
TextWriter log)
{
if (message.To == null)
{
//message = null
return;
}
else
{
//Retrieve the message body regardless of the content as a stream
Stream stream = message.GetBody<Stream>();
StreamReader reader;
if (stream != null)
reader = new StreamReader(stream);
else
reader = null;
string s;
Message serviceMessage;
if ( reader != null )
{
s = reader.ReadToEnd();
serviceMessage = new Microsoft.Azure.Devices.Message(Encoding.ASCII.GetBytes(s));
}
else
{
serviceMessage = new Microsoft.Azure.Devices.Message();
}
foreach (KeyValuePair<string, object> property in message.Properties)
{
serviceMessage.Properties.Add(property.Key, property.Value.ToString());
}
SendToIoTHub(message.To.ToString(), serviceMessage);
}
}
static async void SendToIoTHub(string target, Microsoft.Azure.Devices.Message message)
{
// Write it back out to the target device
ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(IoTHubConnectionString);
var serviceMessage = message;
serviceMessage.Ack = DeliveryAcknowledgement.Full;
serviceMessage.MessageId = Guid.NewGuid().ToString();
try
{
await serviceClient.SendAsync(target, serviceMessage);
}
catch
{
await serviceClient.CloseAsync();
return;
}
await serviceClient.CloseAsync();
}

Push Notification using RHMAP

I am trying to send push notification using RHMAP to iOS devices. Can anyone please let me know how to send push notification to specific device ID using RHMAP. I am able to send notification to all the devices but not to a specific device.
I couldn't find anything regarding this in RHMAP documentation also.
RHMAP has a lot of documented material and examples about push notifications.
The information that you are looking for is in the section "2.3.4.2. Recipient filtering"[1] of the Product features documentation.
"alias - user identification, such as a user name or an e-mail representing a single person (with possibly multiple devices). Intended to enable a unicast communication model."
For more information and examples you can check to fh.push client API[2] and fh.push cloud API[3]. There you can find the following example with filter criteria in the push notifications.
Push a message for specific deviceType in a specific Client App
var message = {
alert: "hello from FH"
},
options = {
apps: ["3uzl1ebi6utciy56majgqlj8"], // list of App IDs
criteria: {
deviceType: "android"
}
};
$fh.push(message, options,
function (err, res) {
if (err) {
console.log(err.toString());
} else {
console.log("status : " + res.status);
}
});
In the example above, for you implement your method using alias, you need to change the deviceType for alias as the following example.
alias: [] // Set here the alias that you are looking for
The following is an example for Android.
import com.feedhenry.sdk.PushConfig;
..........
......
...
private void register() {
PushConfig p = new PushConfig();
p.setAlias("my-alias");
FH.pushRegister(p, new FHActCallback() {
#Override
public void success(FHResponse fhResponse) {
startActivity(new Intent(RegisterActivity.this, MessagesActivity.class));
}
#Override
public void fail(FHResponse fhResponse) {
Toast.makeText(getApplicationContext(),
fhResponse.getErrorMessage(), Toast.LENGTH_SHORT).show();
finish();
}
});
}
[1] - https://access.redhat.com/documentation/en-us/red_hat_mobile_application_platform_hosted/3/html-single/product_features/#sending-notifications
[2] - https://access.redhat.com/documentation/en-us/red_hat_mobile_application_platform_hosted/3/html-single/client_api/#fh-push
[3] - https://access.redhat.com/documentation/en-us/red_hat_mobile_application_platform_hosted/3/html-single/cloud_api/#fh-push
You can set an alias for the device and use that alias to send the notification to it.
As you don't mention which SDK (Objective-C, Swift, Cordova) are you using I can't help you on how to set the alias as it's different for each of them, if you provide more information I can try to help you.

Detect if Firebase connection is lost/regained

Is there a strategy that would work within the current Firebase offering to detect if the server connection is lost and/or regained?
I'm considering some offline contingencies for mobile devices and I would like a reliable means to determine when the Firebase data layer is available.
This is a commonly requested feature, and we just released an API update to let you do this!
var firebaseRef = new Firebase('http://INSTANCE.firebaseio.com');
firebaseRef.child('.info/connected').on('value', function(connectedSnap) {
if (connectedSnap.val() === true) {
/* we're connected! */
} else {
/* we're disconnected! */
}
});
Full docs are available at https://firebase.google.com/docs/database/web/offline-capabilities.
Updated:
For many presence-related features, it is useful for a client to know when it is online or offline. Firebase Realtime Database clients provide a special location at /.info/connected which is updated every time the client's connection state changes. Here is an example:
DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
System.out.println("connected");
} else {
System.out.println("not connected");
}
}
#Override
public void onCancelled(DatabaseError error) {
System.err.println("Listener was cancelled");
}
});
I guess this changed in the last couple of months. Currently the instructions are here:
https://firebase.google.com/docs/database/web/offline-capabilities
In summation:
var presenceRef = firebase.database().ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");
and:
var connectedRef = firebase.database().ref(".info/connected");
connectedRef.on("value", function(snap) {
if (snap.val() === true) {
alert("connected");
} else {
alert("not connected");
}
});
I'll admit I don't know a lot about how references are set, or what that means (are you making them out of thin air or do you have to have already created them beforehand?) or which one of those would trigger something on the server as opposed to something on the front end, but if the link is still current when you read this, a little more reading might help.
For android you can make user offline by just a single function called onDisconnect()
I did this in one of my chat app where user needs to get offline automatically if network connection lost or user disconnected from Internet
DatabaseReference presenceRef = FirebaseDatabase.getInstance().getReference("USERS/24/online_status");
presenceRef.onDisconnect().setValue(0);
On disconnecting from network Here I am making online_status 0 of user whose Id is 24 in firebase.
getReference("USERS/24/online_status") is the path to the value you need to update on offline/online.
You can read about it in offline capabilities
Note that firebase takes time around 2-10 minutes to execute onDisconnect() function.
firebase for web
firebase.database().ref(".info/connected").on("value",(snap)=> {});
The suggested solution didn't work for me, so I decided to check the connection by writing and reading 'health/check' value. This is the code:
const config = {databaseURL: `https://${projectName.trim()}.firebaseio.com/`};
//if app was already initialised delete it
if (firebase.apps.length) {
await firebase.app().delete();
}
// initialise app
let cloud = firebase.initializeApp(config).database();
// checking connection with the app/database
let connectionRef = cloud.ref('health');
connectionRef.set('check')
.then(() => {
return connectionRef.once("value");
})
.then(async (snap) => {
if (snap.val() === 'check') {
// clear the check input
await connectionRef.remove();
// do smth here becasue it works
}
});

Resources