Scanning for beacons using universal beacon library - bluetooth-lowenergy

I am trying to implement a mobile app (on iPhone) that just scans for beacons and displays a notification for each one. I am a noob with beacons/bluetooth.
I implemented it using the universal beacon library (https://github.com/andijakl/universal-beacon) and i've attached my ios bluetooth implementation.
my problem is that i receive about 12 beacon added events even though i only have two (I assume it is picking up all my other bluetooth devices). I also only receive the local name in the advertisement_received event.
My questions are:
how do I distinguish that it is a beacon being added?
how do i get the unique id an url from the beacon? (they are kontakt beacons)
Thanks for any help.
My beacon service:
public BeaconService()
{
// get the platform-specific provider
var provider = RootWorkItem.Services.Get<IBluetoothPacketProvider>();
if (null != provider)
{
// create a beacon manager, giving it an invoker to marshal collection changes to the UI thread
_manager = new BeaconManager(provider, Device.BeginInvokeOnMainThread);
_manager.Start();
_manager.BeaconAdded += _manager_BeaconAdded;
provider.AdvertisementPacketReceived += Provider_AdvertisementPacketReceived;
}
}
My ios bluetooth implementation:
public class iOSBluetoothPacketProvider : CocoaBluetoothPacketProvider { }
public class CocoaBluetoothPacketProvider : NSObject, IBluetoothPacketProvider
{
public event EventHandler<BLEAdvertisementPacketArgs> AdvertisementPacketReceived;
public event EventHandler<BTError> WatcherStopped;
private readonly CocoaBluetoothCentralDelegate centralDelegate;
private readonly CBCentralManager central;
public CocoaBluetoothPacketProvider()
{
Debug.WriteLine("BluetoothPacketProvider()");
centralDelegate = new CocoaBluetoothCentralDelegate();
central = new CBCentralManager(centralDelegate, null);
}
private void ScanCallback_OnAdvertisementPacketReceived(object sender, BLEAdvertisementPacketArgs e)
{
AdvertisementPacketReceived?.Invoke(this, e);
}
public void Start()
{
Debug.WriteLine("BluetoothPacketProvider:Start()");
centralDelegate.OnAdvertisementPacketReceived += ScanCallback_OnAdvertisementPacketReceived;
// Wait for the PoweredOn state
//if(CBCentralManagerState.PoweredOn == central.State) {
// central.ScanForPeripherals(peripheralUuids: new CBUUID[] { },
// options: new PeripheralScanningOptions { AllowDuplicatesKey = false });
//}
}
public void Stop()
{
Debug.WriteLine("BluetoothPacketProvider:Stop()");
centralDelegate.OnAdvertisementPacketReceived -= ScanCallback_OnAdvertisementPacketReceived;
central.StopScan();
WatcherStopped?.Invoke(sender: this, e: new BTError(BTError.BluetoothError.Success));
}
}
internal class CocoaBluetoothCentralDelegate : CBCentralManagerDelegate
{
public event EventHandler<BLEAdvertisementPacketArgs> OnAdvertisementPacketReceived;
#region CBCentralManagerDelegate
public override void ConnectedPeripheral(CBCentralManager central, CBPeripheral peripheral)
{
Debug.WriteLine($"ConnectedPeripheral(CBCentralManager central, CBPeripheral {peripheral})");
}
public override void DisconnectedPeripheral(CBCentralManager central, CBPeripheral peripheral, NSError error)
{
Debug.WriteLine($"DisconnectedPeripheral(CBCentralManager central, CBPeripheral {peripheral}, NSError {error})");
}
public override void DiscoveredPeripheral(CBCentralManager central, CBPeripheral peripheral, NSDictionary advertisementData, NSNumber RSSI)
{
Debug.WriteLine($"Cocoa peripheral {peripheral}");
Debug.WriteLine($"Cocoa advertisementData {advertisementData}");
Debug.WriteLine($"Cocoa RSSI {RSSI}");
var bLEAdvertisementPacket = new BLEAdvertisementPacket()
{
Advertisement = new BLEAdvertisement()
{
LocalName = peripheral.Name,
ServiceUuids = new List<Guid>(),
DataSections = new List<BLEAdvertisementDataSection>(),
ManufacturerData = new List<BLEManufacturerData>()
},
AdvertisementType = BLEAdvertisementType.ScanResponse,
BluetoothAddress = (ulong)peripheral.Identifier.GetHashCode(),
RawSignalStrengthInDBm = RSSI.Int16Value,
Timestamp = DateTimeOffset.Now
};
//https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataserviceuuidskey
//if (advertisementData.ContainsKey(CBAdvertisement.DataServiceUUIDsKey))
//{
// bLEAdvertisementPacket.Advertisement.ServiceUuids.Add(
// item: new BLEManufacturerData(packetType: BLEPacketType.UUID16List,
// data: (advertisementData[CBAdvertisement.DataServiceUUIDsKey])));
//}
//https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataservicedatakey
//if (advertisementData.ContainsKey(CBAdvertisement.DataServiceDataKey))
//{
// bLEAdvertisementPacket.Advertisement.DataSections.Add(
// item: new BLEManufacturerData(packetType: BLEPacketType.ServiceData,
// data: advertisementData[CBAdvertisement.DataServiceDataKey]));
//}
//https://developer.apple.com/documentation/corebluetooth/cbadvertisementdatamanufacturerdatakey
if (advertisementData.ContainsKey(CBAdvertisement.DataManufacturerDataKey))
{
bLEAdvertisementPacket.Advertisement.ManufacturerData.Add(
item: new BLEManufacturerData(packetType: BLEPacketType.ManufacturerData,
data: (advertisementData[CBAdvertisement.DataManufacturerDataKey]
as NSData).ToArray()));
}
// Missing CBAdvertisement.DataTxPowerLevelKey
var bLEAdvertisementPacketArgs = new BLEAdvertisementPacketArgs(data: bLEAdvertisementPacket);
OnAdvertisementPacketReceived?.Invoke(this, bLEAdvertisementPacketArgs);
}
public override void FailedToConnectPeripheral(CBCentralManager central, CBPeripheral peripheral, NSError error)
{
Debug.WriteLine($"FailedToConnectPeripheral(CBCentralManager central, CBPeripheral {peripheral}, NSError {error})");
}
public override void UpdatedState(CBCentralManager central)
{
switch (central.State)
{
case CBCentralManagerState.Unknown:
Debug.WriteLine("CBCentralManagerState.Unknown");
break;
case CBCentralManagerState.Resetting:
Debug.WriteLine("CBCentralManagerState.Resetting");
break;
case CBCentralManagerState.Unsupported:
Debug.WriteLine("CBCentralManagerState.Unsupported");
break;
case CBCentralManagerState.Unauthorized:
Debug.WriteLine("CBCentralManagerState.Unauthorized");
break;
case CBCentralManagerState.PoweredOff:
Debug.WriteLine("CBCentralManagerState.PoweredOff");
break;
case CBCentralManagerState.PoweredOn:
Debug.WriteLine("CBCentralManagerState.PoweredOn");
central.ScanForPeripherals(peripheralUuids: new CBUUID[] { },
options: new PeripheralScanningOptions { AllowDuplicatesKey = true });
break;
default:
throw new NotImplementedException();
}
}
public override void WillRestoreState(CBCentralManager central, NSDictionary dict)
{
Debug.WriteLine($"WillRestoreState(CBCentralManager central, NSDictionary {dict})");
}
#endregion CBCentralManagerDelegate
}

So in case anyone is looking for this. The universal beacon library does not have an ios implementation that converts the ios packets to the universal packets. This need to be implemented.
how do I distinguish that it is a beacon being added?
I look for the Eddystone packets and if found I add to the observable list.
how do i get the unique id an url from the beacon? (they are kontakt beacons)
You need to loop through the advertisementData sent with the advertisement and create a BLEAdvertisementDataSection. copy the frame data as NSData.

Related

need to send local notification even after killing my app using xamarin.forms

I am using xamarin forms for my app. I need to send local notification based on SQLite table for ios. I am using the following code:
AppDelegate.cs:
using System;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Firebase.Database;
using Firebase.CloudMessaging;
using UserNotifications;
using Microsoft.AppCenter.Crashes;
using Plugin.Permissions;
using Plugin.Permissions.Abstractions;
using CoreTelephony;
using Firebase.Auth;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace MyApp.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
Firebase.Core.App.Configure();
Database.DefaultInstance.PersistenceEnabled = true;
LoadApplication(new MyApp.App());
RegisterForRemoteNotifications();
Messaging.SharedInstance.Delegate = this;
if (UNUserNotificationCenter.Current != null)
{
UNUserNotificationCenter.Current.Delegate = new UserNotificationCenterDelegate();
}
if (UNUserNotificationCenter.Current != null)
{
UNUserNotificationCenter.Current.Delegate = new UserNotificationCenterDelegate();
}
Xamarin.FormsMaps.Init();
UIApplication.SharedApplication.IdleTimerDisabled = true;
UIApplication.SharedApplication.SetMinimumBackgroundFetchInterval(UIApplication.BackgroundFetchIntervalMinimum);
return base.FinishedLaunching(app, options);
}
public override bool ShouldRestoreApplicationState(UIApplication application, NSCoder coder)
{
return true;
}
private async System.Threading.Tasks.Task AcessPermissionsAsync()
{
try
{
await CrossPermissions.Current.RequestPermissionsAsync(Permission.Camera);
}
catch(Exception ex)
{
Crashes.TrackError(ex);
}
}
private void RegisterForRemoteNotifications()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
UNUserNotificationCenter.Current.RemoveAllPendingNotificationRequests(); // To remove all pending notifications which are not delivered yet but scheduled.
UNUserNotificationCenter.Current.RemoveAllDeliveredNotifications(); // To remove all delivered notifications
}
else
{
UIApplication.SharedApplication.CancelAllLocalNotifications();
}
// Register your app for remote notifications.
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, async (granted, error) =>
{
Console.WriteLine(granted);
await System.Threading.Tasks.Task.Delay(500);
await AcessPermissionsAsync();
});
}
else
{
// iOS 9 or before
var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
public override void DidEnterBackground(UIApplication uiApplication)
{
Messaging.SharedInstance.ShouldEstablishDirectChannel = false;
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Messaging.SharedInstance.ApnsToken = deviceToken;
}
[Export("messaging:didReceiveRegistrationToken:")]
public void DidReceiveRegistrationToken(Messaging messaging, string fcmToken)
{
Xamarin.Forms.Application.Current.Properties["Fcmtocken"] = Messaging.SharedInstance.FcmToken ?? "";
Xamarin.Forms.Application.Current.SavePropertiesAsync();
System.Diagnostics.Debug.WriteLine($"######Token###### : {fcmToken}");
DeviceId.FCMTocken = fcmToken;
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Print full message.
Console.WriteLine(userInfo);
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
try
{
Console.WriteLine(userInfo);
completionHandler(UIBackgroundFetchResult.NewData);
}
catch(Exception ex)
{
Crashes.TrackError(ex);
}
}
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow)
{
switch (Device.Idiom)
{
case TargetIdiom.Phone:
return UIInterfaceOrientationMask.Portrait;
case TargetIdiom.Tablet:
return UIInterfaceOrientationMask.Landscape;
default:
return UIInterfaceOrientationMask.Portrait;
}
}
private void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var newExc = new Exception("CurrentDomainOnUnhandledException", unhandledExceptionEventArgs.ExceptionObject as Exception);
ExceptionFileWriter.ToLogUnhandledException(newExc);
}
public void DidRefreshRegistrationToken(Messaging messaging, string fcmToken)
{
Xamarin.Forms.Application.Current.Properties["Fcmtocken"] = Messaging.SharedInstance.FcmToken ?? "";
Xamarin.Forms.Application.Current.SavePropertiesAsync();
System.Diagnostics.Debug.WriteLine($"######Token###### : {fcmToken}");
DeviceId.FCMTocken = fcmToken;
}
public override void PerformFetch(UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
{
try
{
var baseService = new DatabaseServices<object, object>();
baseService.NotificationAssetTrip();
}
catch(Exception ex)
{
Logs.LogCreate("PerformFetch:Exception: " + ex.Message.ToString());
}
completionHandler(UIBackgroundFetchResult.NewData);
}
}
}
UserNotificationCenterDelegate.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Foundation;
using iAd;
using ObjCRuntime;
using UIKit;
using UserNotifications;
namespace MyApp.iOS
{
public class UserNotificationCenterDelegate : UNUserNotificationCenterDelegate
{
private DBService dataBase;
#region Constructors
public UserNotificationCenterDelegate()
{
UNUserNotificationCenter.Current.Delegate = this;
}
#endregion
#region Override Methods
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
completionHandler.DynamicInvoke(UNNotificationPresentationOptions.Alert);
}
#endregion
}
}
App.xaml.cs:
private void SendNotificationTripAsset(int tripid)
{
//Forexample i have table with columns like title,subtitle,message means, i give like this
var content = new UserNotifications.UNMutableNotificationContent()
{
Title = title,
Subtitle = subtitle,
Body = message,
Badge = 1
};
var request = UserNotifications.UNNotificationRequest.FromIdentifier(list.TripID.ToString(), content, null);
UserNotifications.UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
{
if (err != null)
{
throw new Exception($"Failed to schedule notification: {err}");
}
});
}
But the notification is not trigged after app is killed(terminated). Can anyone please help me to resolve this issue.
To send a local notification when app is killed, you have to specify the time when should the local notification been sent. In your code, when you create the request, set a proper NNotificationTrigger :
NNotificationTrigger trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(3, false);
var request = UserNotifications.UNNotificationRequest.FromIdentifier(list.TripID.ToString(), content, trigger );
That means this notification will be fired after 3 seconds. You can change the time to whatever you want.
Code will not execute when your app is killed, so if you don't know the specific time to fire the notification or it fires randomly, you should use a remote notification.

Xamarin Forms: Load a contentpage when tap push notification android

I completed receiving test notification from FCM console. Now I am trying to open a page when tapping the notification. Any ideas about how to achieve this? I have searched the internet but can't find a working solution. I am also able to send the notification through the postman.
I handle the notification tapping in following way. The page loading is handled in App.xaml.cs.
On OnCreate():
//Background or killed mode
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
var value = Intent.Extras.GetString(key);
if (key == "webContentList")
{
if (value?.Length > 0)
{
isNotification = true;
LoadApplication(new App(domainname, value));
}
}
}
}
//Foreground mode
if (FirebaseNotificationService.webContentList.ToString() != "")
{
isNotification = true;
LoadApplication(new App(domainname, FirebaseNotificationService.webContentList.ToString()));
FirebaseNotificationService.webContentList = "";
}
//Normal loading
if (!isNotification)
{
LoadApplication(new App(domainname, string.Empty));
}
On FirebaseNotificationService:
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseNotificationService : FirebaseMessagingService
{
public static string webContentList = "";
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
webContentList = message.Data["webContentList"];
try
{
SendNotificatios(message.GetNotification().Body, message.GetNotification().Title);
}
catch (Exception ex)
{
Console.WriteLine("Error:>>" + ex);
}
}
public void SendNotificatios(string body, string Header)
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
var notificationBuilder = new Android.App.Notification.Builder(this, Utils.CHANNEL_ID)
.SetContentTitle(Header)
.SetSmallIcon(Resource.Drawable.icon)
.SetContentText(body)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent);
var notificationManager = NotificationManager.FromContext(this);
notificationManager.Notify(0, notificationBuilder.Build());
}
else
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
var notificationBuilder = new Android.App.Notification.Builder(this, Utils.CHANNEL_ID)
.SetContentTitle(Header)
.SetSmallIcon(Resource.Drawable.icon)
.SetContentText(body)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent)
.SetChannelId(Utils.CHANNEL_ID);
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
return;
}
var channel = new NotificationChannel(Utils.CHANNEL_ID, "FCM Notifications", NotificationImportance.High)
{
Description = "Firebase Cloud Messages appear in this channel"
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
notificationManager.Notify(0, notificationBuilder.Build());
}
}
I don't know what's your actual Firebase implementation, but this might help you.
There is a nice package for Firebase in Xamarin Forms that we use in our production App made by CrossGeeks team. Its working great and has all handlers for your needs. This works with iOS and Android and you don't need to write platform specific code, only configuration and some code in AppDelegate.cs and MainActivity.cs
https://github.com/CrossGeeks/FirebasePushNotificationPlugin/blob/master/docs/FirebaseNotifications.md#notification-events
I wrote a simple PushNotificationService that processes automatic refresh and/or pushes new pages considering push notif data.
When app is closed and user clicks on the notification, I store the push notif data using Akavache.
CrossFirebasePushNotification.Current.OnNotificationOpened += async (s, p) =>
{
if (App.AppBeenResumed)
{
await BlobCache.UserAccount.InsertObject("pushNotifData", p.Data);
}
else
{
await ProcessReceivedPushNotification(p.Data);
}
};
And on the landing page of the app, I check if there is an existing push notif data in the OnAppearing method of the page.
protected override void OnAppearing()
{
base.OnAppearing();
App.AppBeenResumed = false;
HandlePushNotificationIfExists();
}
private async void HandlePushNotificationIfExists()
{
IDictionary<string, object> pushNotifData;
try
{
pushNotifData = await BlobCache.UserAccount.GetObject<IDictionary<string, object>>("pushNotifData");
}
catch (KeyNotFoundException)
{
pushNotifData = null;
}
if (pushNotifData == null) return;
await BlobCache.UserAccount.InvalidateAllObjects<IDictionary<string, object>>();
await PushNotificationService.ProcessReceivedPushNotification(pushNotifData);
}
In the ProcessReceivedPushNotification you can do whatever you want ... push directly the page or whatever... call another service that will do the job of pushing a new page and some business process.
Note that App.AppBeenResumed is a static bool to determine if App has been started or resumed to handle correctly the process of treatment of the push notif (process it instant or store it in blobcache to treat it later when landing page is Appearing).
In MainActivity.cs :
protected override void OnCreate(Bundle bundle)
{
...
LoadApplication(new App(true));
}
In the App.cs :
public App(bool beenResumedOrStarted)
{
...
AppBeenResumed = beenResumedOrStarted;
...
}
protected override void OnResume()
{
AppBeenResumed = false;
}
protected override void OnSleep()
{
//iOS states are not the same so always false when device is iOS
AppBeenResumed = Device.RuntimePlatform != Device.iOS;
}

Task running once a day on Xamarin Forms

I started using xamarin a few months ago, and, until now, I didn't have the need of doing something like this.
I'm developing an app that, once a day, should run a WCF web service and verify if an information is true. If it is true, it should show a notification on the device.
My problem is that I don't know how to perform it, i've read about backgrounding and schedule tasks, but I didn't understand well how can I perform this. How can I do it using Xamarin.Forms?
Thank you!
For Android, a solution needs 4 components:
AlarmManager to set daily check schedule
BroadCastReceiver to receive the daily trigger and call the IntentService
IntentService to execute awaitable calls
OnBootReceiver to ensure alarms are set again after a device reboot
For iOS, you will most likely need remote push notifications.
Some sample code below for the Android components:
AlarmManager - Setting the Alarm
[assembly: Dependency(typeof(AlarmHelper))] // above the namespace
...
class AlarmHelper: IAlarm
{
var now = Calendar.Instance;
var alarmTime = Calendar.Instance;
alarmTime.Set(CalendarField.HourOfDay, settings.AlarmHour); // Set Alarm start Hour
alarmTime.Set(CalendarField.Minute, settings.AlarmMinutes); // Set Alarm Start Minutes
if (alarmTime.Before(now))
{
alarmTime.Add(CalendarField.Hour, 24);
}
var intent = new Intent(Android.App.Application.Context, typeof(ScheduledAlarmHandler));
var pendingIntent = PendingIntent.GetBroadcast(Android.App.Application.Context, 0, intent, PendingIntentFlags.CancelCurrent);
var alarmManager = Android.App.Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.SetRepeating(AlarmType.RtcWakeup, alarmTime.TimeInMillis, AlarmManager.IntervalDay, pendingIntent);
}
BroadCastReceiver - Receiving the Alarm
[BroadcastReceiver]
class ScheduledAlarmHandler : WakefulBroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Console.WriteLine("ScheduledAlarmHandler", "Starting service #" + SystemClock.ElapsedRealtime());
Intent service = new Intent(context, typeof(WakefulAPIService));
StartWakefulService(context, service);
}
}
IntentService - Executing awaitable calls
[Service]
[IntentFilter(new String[] { "com.test.testApp.WakefulAPIService" })]
class WakefulAPIService : IntentService
{
protected override void OnHandleIntent(Intent intent)
{
// Your API Call code here
Console.WriteLine("WakefulAPIService", "Completed service # " + SystemClock.ElapsedRealtime());
Android.Support.V4.Content.WakefulBroadcastReceiver.CompleteWakefulIntent(intent);
}
}
OnBootReceiver - Ensuring alarms are set again after a device reboot
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "android.intent.action.BOOT_COMPLETED", "android.intent.action.QUICKBOOT_POWERON" })]
class OnBootReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Console.WriteLine("On Boot Reveiver", "Alarm Set Again after Reboot");
var alarmHelper = new AlarmHelper();
alarmHelper.SetAlarm();
}
}
The required permissions for this to work are 'RECEIVE_BOOT_COMPLETED' and 'WAKE_LOCK'
In this case for Android you can use you can use JobScheduler, see this class
[Service(Name = "com.xamarin.samples.downloadscheduler.DownloadJob",
Permission = "android.permission.BIND_JOB_SERVICE")]
public class DownloadJob : JobService
{
public override bool OnStartJob(JobParameters jobParams)
{
Task.Run(() =>
{
//Your periodic task here
});
return true;
}
public override bool OnStopJob(JobParameters jobParams)
{
//true so we re-schedule the task
return true;
}
}
Then you can create a Factory to call this service.
public static class ReadLocationSchedulerFactory
{
public static JobInfo.Builder CreateJobBuilderUsingJobId<T>(this Context context, int jobId) where T : JobService
{
var javaClass = Java.Lang.Class.FromType(typeof(T));
var componentName = new ComponentName(context, javaClass);
return new JobInfo.Builder(jobId, componentName);
}
}
Then in your Main Activity you have to call the Factory.
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App172S.App());
#region Scheduler
var jobBuilder = this.CreateJobBuilderUsingJobId<ReadLocationScheduler>(152);
//This means each 20 mins
jobBuilder.SetPeriodic(20 * 60 * 1000);
//Persists over phone restarts
jobBuilder.SetPersisted(true);
//If Fails re-try each 2 mins
jobBuilder.SetBackoffCriteria(120 * 1000, BackoffPolicy.Linear);
var jobInfo = jobBuilder.Build();
var jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
jobScheduler.Cancel(152);
var scheduleResult = jobScheduler.Schedule(jobInfo);
if (JobScheduler.ResultSuccess == scheduleResult)
{
//If OK maybe show a msg
}
else
{
//If Failed do something
}
#endregion
}

QR detection using ZXing with Vuforia in Unity

We have implemented the QR detection functionality using ZXing.dll in Unity 5.3.4f1 with Vuforia Unity SDK 5.5.9. We have a QR detection script on GameObject which remains active throughout the app and using below mentioned (QRScanner.cs) code ( as mentioned on Unity Zxing QR code scanner integration ).
We are also using Vuforia for image detection (50 image targets) in the same scene where QR detection is expected. The Vuforia plugin is getting enabled / disabled multiple times as per our requirement. Both the image and QR detection is working perfectly for us on Android and iOS devices until the app is in focus. Whenever VuforiaBehaviour gets disabled and enabled, QR detection stops working after that. QRScanner script always receives null data after the app is resumed or AR camera is reloaded. We have tried keeping our QR detection script on AR camera prefab and also tried
qcarBehaviour.RegisterTrackablesUpdatedCallback(OnTrackablesUpdated);
qcarBehaviour.RegisterQCARStartedCallback(OnTrackablesUpdated);
callbacks every time AR camera starts but with no success. The QR detection stops working completely after pausing Vuforia plugin for any reason.
Does anybody have any idea how to fix this issue?
QRScanner.cs
using UnityEngine;
using System;
using System.Collections;
using Vuforia;
using System.Threading;
using ZXing;
using ZXing.QrCode;
using ZXing.Common;
/* ///////////////// QR detection does not work in editor //////////////// */
[AddComponentMenu("System/QRScanner")]
public class QRScanner : MonoBehaviour
{
private bool cameraInitialized;
private BarcodeReader barCodeReader;
public AppManager camScript;
void Start()
{
barCodeReader = new BarcodeReader();
StartCoroutine(InitializeCamera());
}
private IEnumerator InitializeCamera()
{
// Waiting a little seem to avoid the Vuforia's crashes.
yield return new WaitForSeconds(3f);
var isFrameFormatSet = CameraDevice.Instance.SetFrameFormat(Image.PIXEL_FORMAT.RGB888, true);
Debug.Log(String.Format("FormatSet : {0}", isFrameFormatSet));
// Force autofocus.
// var isAutoFocus = CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_CONTINUOUSAUTO);
// if (!isAutoFocus)
// {
// CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_NORMAL);
// }
// Debug.Log(String.Format("AutoFocus : {0}", isAutoFocus));
cameraInitialized = true;
}
private void Update()
{
if (cameraInitialized)
{
try
{
var cameraFeed = CameraDevice.Instance.GetCameraImage(Image.PIXEL_FORMAT.RGB888);
if (cameraFeed == null)
{
return;
}
var data = barCodeReader.Decode(cameraFeed.Pixels, cameraFeed.BufferWidth, cameraFeed.BufferHeight, RGBLuminanceSource.BitmapFormat.RGB24);
if (data != null)
{
// QRCode detected.
Debug.Log(data.Text);
Application.OpenURL (data.Text); // our function to call and pass url as text
data = null; // clear data
}
else
{
Debug.Log("No QR code detected !");
}
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
}
}
}
i have this problem to
but i fix that with place code on ARcam
using UnityEngine;
using System.Collections;
using Vuforia;
public class CameraSettings : MonoBehaviour
{
#region PRIVATE_MEMBERS
private bool mVuforiaStarted = false;
private bool mAutofocusEnabled = true;
private bool mFlashTorchEnabled = false;
private CameraDevice.CameraDirection mActiveDirection = CameraDevice.CameraDirection.CAMERA_DEFAULT;
#endregion //PRIVATE_MEMBERS
#region MONOBEHAVIOUR_METHODS
void Start () {
Debug.Log("CameraSettings Start");
VuforiaAbstractBehaviour vuforia = FindObjectOfType<VuforiaAbstractBehaviour>();
VuforiaARController.Instance.RegisterVuforiaStartedCallback(OnVuforiaStarted);
VuforiaARController.Instance.RegisterOnPauseCallback(OnPaused);
VuforiaARController.Instance.RegisterTrackablesUpdatedCallback (OnTrack);
//VuforiaARController.Instance.RegisterVideoBgEventHandler(BgEventHandler);
}
#endregion // MONOBEHAVIOUR_METHODS
#region PUBLIC_METHODS
public bool IsFlashTorchEnabled()
{
return mFlashTorchEnabled;
}
public void SwitchFlashTorch(bool ON)
{
if (CameraDevice.Instance.SetFlashTorchMode(ON))
{
Debug.Log("Successfully turned flash " + ON);
mFlashTorchEnabled = ON;
}
else
{
Debug.Log("Failed to set the flash torch " + ON);
mFlashTorchEnabled = false;
}
}
public bool IsAutofocusEnabled()
{
return mAutofocusEnabled;
}
public void SwitchAutofocus(bool ON)
{
if (ON)
{
if (CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_CONTINUOUSAUTO))
{
Debug.Log("Successfully enabled continuous autofocus.");
mAutofocusEnabled = true;
}
else
{
// Fallback to normal focus mode
Debug.Log("Failed to enable continuous autofocus, switching to normal focus mode");
mAutofocusEnabled = false;
CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_NORMAL);
}
}
else
{
Debug.Log("Disabling continuous autofocus (enabling normal focus mode).");
mAutofocusEnabled = false;
CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_NORMAL);
}
}
public void TriggerAutofocusEvent()
{
// Trigger an autofocus event
CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_TRIGGERAUTO);
// Then restore original focus mode
StartCoroutine(RestoreOriginalFocusMode());
}
public void SelectCamera(CameraDevice.CameraDirection camDir)
{
if (RestartCamera (camDir))
{
mActiveDirection = camDir;
// Upon camera restart, flash is turned off
mFlashTorchEnabled = false;
}
}
public bool IsFrontCameraActive()
{
return (mActiveDirection == CameraDevice.CameraDirection.CAMERA_FRONT);
}
#endregion // PUBLIC_METHODS
#region PRIVATE_METHODS
private void OnTrack() {
//Debug.Log("CameraSettings OnTrack");
}
private void BgEventHandler() {
//Debug.Log("CameraSettings BgEventHandler");
}
private void OnVuforiaStarted() {
//Debug.Log("CameraSettings OnVuforiaStarted");
mVuforiaStarted = true;
// Try enabling continuous autofocus
SwitchAutofocus(true);
//RestartCamera (CameraDevice.CameraDirection.CAMERA_DEFAULT);
}
private void OnPaused(bool paused) {
bool appResumed = !paused;
//Debug.Log("CameraSettings OnPaused");
if (appResumed && mVuforiaStarted)
{
// Restore original focus mode when app is resumed
if (mAutofocusEnabled)
CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_CONTINUOUSAUTO);
else
CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_NORMAL);
// Set the torch flag to false on resume (cause the flash torch is switched off by the OS automatically)
mFlashTorchEnabled = false;
}
}
private IEnumerator RestoreOriginalFocusMode()
{
// Wait 1.5 seconds
yield return new WaitForSeconds(1.5f);
// Restore original focus mode
if (mAutofocusEnabled)
CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_CONTINUOUSAUTO);
else
CameraDevice.Instance.SetFocusMode(CameraDevice.FocusMode.FOCUS_MODE_NORMAL);
}
private bool RestartCamera(CameraDevice.CameraDirection direction)
{
ObjectTracker tracker = TrackerManager.Instance.GetTracker<ObjectTracker>();
if (tracker != null)
tracker.Stop();
CameraDevice.Instance.Stop();
CameraDevice.Instance.Deinit();
if (!CameraDevice.Instance.Init(direction))
{
Debug.Log("Failed to init camera for direction: " + direction.ToString());
return false;
}
if (!CameraDevice.Instance.Start())
{
Debug.Log("Failed to start camera for direction: " + direction.ToString());
return false;
}
if (tracker != null)
{
if (!tracker.Start())
{
Debug.Log("Failed to restart the Tracker.");
return false;
}
}
return true;
}
#endregion // PRIVATE_METHODS
}

Using MailChimp WebHooks feature

Can someone please provide examples of doing this in ASP.NET. We want to do some MailCHimp – internal database synchronization and plan to do this using webhooks feature but we just can’t get it work. We want to use web hooks to synch data when someone unsubscribes from mail chimp.
Another thing to address is security. How can we secure this page from being accessed by malicious users?
Here is a piece of code that works for us. This is fairly simple but it did take us some experimenting to get it to work.
if (Request.Form["type"] != null && Request.Form["type"] == "unsubscribe")
{
string email = Request.Form["data[merges][EMAIL]"];
//now you can do insert/update data in your local database
}
Check out the API documentation for more details http://apidocs.mailchimp.com/webhooks/
Regarding security you can do a ton of stuff but it depends on how deep you want to go. One thing I’d recommend is checking your IIS logs and finding which IP address/user agent is used by mail chimp to trigger web hooks and then just block this page for all other IP addresses except for this. There are probably other things you can do to additionally secure this like using page name that is not easily guessed (f3jijselife.aspx is far better than webhooks.aspx)
I just implemented this recently based on the PHP code they provided here's the skeleton... I took out the actual implementation but should be useful hopefully
public class MailChimpWebHook : IHttpHandler
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(MailChimpWebHook));
private const string Key = "xxxx";
private const string ParamKey = "key";
private const string ParamType = "type";
private const string ParamListId = "data[list_id]";
private const string ParamListIdNew = "data[new_id]";
private const string ParamEmail = "data[email]";
private const string ParamOldEmail = "data[new_email]";
private const string ParamNewEmail = "data[old_email]";
private const string ParamProfileEmail = "data[merges][EMAIL]";
private const string ParamProfileFirstName = "data[merges][FNAME]";
private const string ParamProfileLastName = "data[merges][LNAME]";
private const string ParamProfileGroups = "data[merges][INTERESTS]";
private const string TypeSubscribe = "subscribe";
private const string TypeUnsubscribe = "unsubscribe";
private const string TypeCleaned = "cleaned";
private const string TypeUpdateEmail = "upemail";
private const string TypeProfileUpdate = "profile";
public void ProcessRequest(HttpContext context)
{
Logger.Info("==================[ Incoming Request ]==================");
if (string.IsNullOrEmpty(context.Request[ParamKey]))
{
Logger.Warn("No security key specified, ignoring request");
}
else if (context.Request[ParamKey] != Key)
{
Logger.WarnFormat("Security key specified, but not correct. Wanted: '{0}' | , but received '{1}'", Key, context.Request[ParamKey]);
}
else
{
//process the request
Logger.InfoFormat("Processing a '{0}' request...", context.Request[ParamType]);
try
{
switch (context.Request[ParamType])
{
case TypeSubscribe:
Subscribe(context.Request);
break;
case TypeUnsubscribe:
Unsubscribe(context.Request);
break;
case TypeCleaned:
Cleaned(context.Request);
break;
case TypeUpdateEmail:
UpdateEmail(context.Request);
break;
case TypeProfileUpdate:
UpdateProfile(context.Request);
break;
default:
Logger.WarnFormat("Request type '{0}' unknown, ignoring.", context.Request[ParamType]);
break;
}
}
catch (Exception e)
{
Logger.Error("There was an error processing the callback", e);
}
}
Logger.Info("Finished processing request.");
}
private void UpdateProfile(HttpRequest httpRequest)
{
Logger.Info("Processing update profile request!");
#region [ sample data structure ]
// "type": "profile",
// "fired_at": "2009-03-26 21:31:21",
// "data[id]": "8a25ff1d98",
// "data[list_id]": "a6b5da1054",
// "data[email]": "api#mailchimp.com",
// "data[email_type]": "html",
// "data[merges][EMAIL]": "api#mailchimp.com",
// "data[merges][FNAME]": "MailChimp",
// "data[merges][LNAME]": "API",
// "data[merges][INTERESTS]": "Group1,Group2",
// "data[ip_opt]": "10.20.10.30"
#endregion
}
private void UpdateEmail(HttpRequest httpRequest)
{
Logger.Info("Processing update email request!");
#region [ sample data structure ]
// "type": "upemail",
// "fired_at": "2009-03-26\ 22:15:09",
// "data[list_id]": "a6b5da1054",
// "data[new_id]": "51da8c3259",
// "data[new_email]": "api+new#mailchimp.com",
// "data[old_email]": "api+old#mailchimp.com"
#endregion
}
private void Cleaned(HttpRequest httpRequest)
{
Logger.Info("Processing cleaned email request!");
#region [ sample data structure ]
// "type": "cleaned",
// "fired_at": "2009-03-26 22:01:00",
// "data[list_id]": "a6b5da1054",
// "data[campaign_id]": "4fjk2ma9xd",
// "data[reason]": "hard",
// "data[email]": "api+cleaned#mailchimp.com"
#endregion
}
private void Unsubscribe(HttpRequest httpRequest)
{
Logger.Info("Processing unsubscribe...");
#region [ sample data structure ]
// "type": "unsubscribe",
// "fired_at": "2009-03-26 21:40:57",
// "data[action]": "unsub",
// "data[reason]": "manual",
// "data[id]": "8a25ff1d98",
// "data[list_id]": "a6b5da1054",
// "data[email]": "api+unsub#mailchimp.com",
// "data[email_type]": "html",
// "data[merges][EMAIL]": "api+unsub#mailchimp.com",
// "data[merges][FNAME]": "MailChimp",
// "data[merges][LNAME]": "API",
// "data[merges][INTERESTS]": "Group1,Group2",
// "data[ip_opt]": "10.20.10.30",
// "data[campaign_id]": "cb398d21d2",
// "data[reason]": "hard"
#endregion
}
private void Subscribe(HttpRequest httpRequest)
{
Logger.Info("Processing subscribe...");
#region [ sample data structure ]
// "type": "subscribe",
// "fired_at": "2009-03-26 21:35:57",
// "data[id]": "8a25ff1d98",
// "data[list_id]": "a6b5da1054",
// "data[email]": "api#mailchimp.com",
// "data[email_type]": "html",
// "data[merges][EMAIL]": "api#mailchimp.com",
// "data[merges][FNAME]": "MailChimp",
// "data[merges][LNAME]": "API",
// "data[merges][INTERESTS]": "Group1,Group2",
// "data[ip_opt]": "10.20.10.30",
// "data[ip_signup]": "10.20.10.30"
#endregion
}
public bool IsReusable
{
get
{
return false;
}
}
}
I'm using C# WebAPI and the solution for me was to use the FormDataCollection object from the body of the POST MailChimp sends with the webhook.
using System.Net.Http.Formatting;
[HttpPost]
[Route("mailchimp/subscriber")]
public IHttpActionResult Post([FromBody] FormDataCollection data)
{
if (data != null)
{
string type = data.Get("type");
if (!string.IsNullOrWhiteSpace(type))
{
string listId = data.Get("data[list_id]");
string id = data.Get("data[id]");
string firstName = data.Get("data[merges][FNAME]");
string lastName = data.Get("data[merges][LNAME]");
string email = data.Get("data[email]");
if (!string.IsNullOrWhiteSpace(email))
{
// Do something with the subscriber
}
}
}
}
I fully support the answer by James.
However, when trying to implement a webhook myself, I have discovered that you'll also need to implement a GET method, in order to even be able to create the webhook in MailChimp.
This did the trick for me:
[HttpGet]
[HttpOptions]
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK);
}
MailChimp documentation:
https://developer.mailchimp.com/documentation/mailchimp/guides/about-webhooks/

Resources