Xamarin Forms - Get device location every 10 seconds (when app runs in foreground/background) - xamarin.forms

I have created a Xamarin forms application. The application should periodically (every 10 sec) get the location of the device (iOS and Android). How can I achieve this? I know there are some libraries for example: Xamarin.Essentials, but I can't decide how many times the location should be taken.
It should also be possible to get the local of the device when the Xamarin forms application runs in the background (on IOS and Android).

I passed for that headache in the past, a cross-platform app that update location every x seconds and it should run in the background.
I had developed a template in Xamarin Forms that support Background Location Updates, Background permissions, these capabilities need to be adjusted depending on the OS (iOs/Android).
Check my Github repo here
I used Xamarin Essentials and Messaging Center for this purpose.
Please let me know if the template works for your needs.
Thanks.

You can do it with device timer. The timer will run periodically & will check for location updates and notify if location updated. I have used Xam.Plugins.Notifier to generate local notification on location update.
Here is the code for it :
public partial class MainPage : ContentPage
{
Location oldLocation = null;
CancellationTokenSource cts;
public MainPage()
{
InitializeComponent();
Device.StartTimer(TimeSpan.FromSeconds(10), () =>
{
GetCurrentLocation();
return true;
});
}
protected async override void OnAppearing()
{
base.OnAppearing();
await GetCurrentLocation();
}
async Task GetCurrentLocation()
{
try
{
var request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));
cts = new CancellationTokenSource();
var location = await Geolocation.GetLocationAsync(request, cts.Token);
if (location != null)
{
Debug.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");
if (oldLocation == null)
{
oldLocation = location;
map.MoveToRegion(MapSpan.FromCenterAndRadius(
new Position(location.Latitude, location.Longitude), Distance.FromMiles(0.3)));
}
if (location.Latitude != oldLocation.Latitude || location.Longitude != oldLocation.Longitude)
{
map.MoveToRegion(MapSpan.FromCenterAndRadius(
new Position(location.Latitude, location.Longitude), Distance.FromMiles(0.3)));
oldLocation = location;
double zoomLevel = 0.5;
double latlongDegrees = 360 / (Math.Pow(2, zoomLevel));
if (map.VisibleRegion != null)
{
map.MoveToRegion(new MapSpan(map.VisibleRegion.Center, latlongDegrees, latlongDegrees));
}
var placemarks = await Geocoding.GetPlacemarksAsync(location.Latitude, location.Longitude);
var placemark = placemarks?.FirstOrDefault();
if (placemark != null)
{
var geocodeAddress =
$"AdminArea: {placemark.AdminArea}\n" +
$"CountryCode: {placemark.CountryCode}\n" +
$"CountryName: {placemark.CountryName}\n" +
$"FeatureName: {placemark.FeatureName}\n" +
$"Locality: {placemark.Locality}\n" +
$"PostalCode: {placemark.PostalCode}\n" +
$"SubAdminArea: {placemark.SubAdminArea}\n" +
$"SubLocality: {placemark.SubLocality}\n" +
$"SubThoroughfare: {placemark.SubThoroughfare}\n" +
$"Location : {placemark.Location}\n" +
$"Thoroughfare: {placemark.Thoroughfare}\n";
Debug.WriteLine(geocodeAddress);
}
CrossLocalNotifications.Current.Show("Location Updated", "You checked in to " + placemark.FeatureName + " " + placemark.Locality + " " + placemark.SubLocality, 101, DateTime.Now.AddSeconds(5));
}
}
}
catch (FeatureNotSupportedException)
{
// Handle not supported on device exception
}
catch (FeatureNotEnabledException)
{
// Handle not enabled on device exception
}
catch (PermissionException)
{
// Handle permission exception
}
catch (Exception)
{
// Unable to get location
}
}
}

For Android you can try to start a Service that uses the LocationManager of Android to start listening to Location changes. You can specify a timeinterval and a minimum distance you want to track.
This section helped me fiqure out how to use it. For me it was sending location updates even when the app was suspended (physical device running Android 6.1).
To get the location I made my Service a 'LocationListener' and implemented the ILocationListener-Interface like so:
[Service]
public class TestService : Service, ILocationListener
{
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
// start your location updates with the locationManager here
return StartCommandResult.Sticky; // remember to return sticky for the service to run when app is suspended
}
public override void OnDestroy() { }
...
public void OnLocationChanged(Location location)
{
// react to location changes here
}
public void OnProviderDisabled(string provider) { }
public void OnProviderEnabled(string provider) { }
public void OnStatusChanged(string provider, Availability status, Bundle extras) { }
}
For more information on Backgrounding and how to set up a service read this.
Important to note is that the locationUpdates where not consistantly timed (sometimes took more that 10 seconds), since you just give a minimumTime and the OS processes the Request based on its' capacities. But it wasn't too bad.
Update: this doesnt seem to work for Android 8.0 and above. see here

Related

xamarin forms geolocation won't ask for gps to be turned on if the it's off

i'm trying to get the current location of the phone and then set the google maps view accordingly but if i have the gps off, it will just thrown an error instead of asking me to turn it on.
i have the access fine and coarse location permissions on android.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
var location = await Geolocation.GetLocationAsync(new GeolocationRequest(GeolocationAccuracy.Best));
I suggest you can detect whether GPS Location enable before you use it. I do one sample to check GPS isenbale or disable on Android platform for Xamarin.forms, the setting GPS location will open if GPS location disable.
Creating interface IGpsDependencyService in shared code.
public interface IGpsDependencyService
{
void OpenSettings();
bool IsGpsEnable();
}
Implement this interface on Android Platform, the platform implementations must be registered with the DependencyService, so that Xamarin.Forms can locate them at runtime.
[assembly: Dependency(typeof(GpsDependencyService))]
namespace FormsSample.Droid
{
public class GpsDependencyService : IGpsDependencyService
{
public bool IsGpsEnable()
{
LocationManager locationManager = (LocationManager)Android.App.Application.Context.GetSystemService(Context.LocationService);
return locationManager.IsProviderEnabled(LocationManager.GpsProvider);
}
public void OpenSettings()
{
Intent intent = new Intent(Android.Provider.Settings.ActionLocat‌​ionSourceSettings);
intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.NewTask);
try
{
Android.App.Application.Context.StartActivity(intent);
}
catch (ActivityNotFoundException activityNotFoundException)
{
System.Diagnostics.Debug.WriteLine(activityNotFoundException.Message);
Android.Widget.Toast.MakeText(Android.App.Application.Context, "Error: Gps Activity", Android.Widget.ToastLength.Short).Show();
}
}
}
}
To check GPS location, I use Plugin.Permissions to check location permission.
private async void Button_OnClicked(object sender, EventArgs e)
{
//var results = await CrossGeolocator.Current.GetPositionAsync(TimeSpan.FromSeconds(10));
//LabelGeolocation.Text = "Lat: " + results.Latitude + " Long: " + results.Longitude;
bool IsGpsEnable = Xamarin.Forms.DependencyService.Get<IGpsDependencyService>().IsGpsEnable();
if (!IsGpsEnable)
{
DisplayAlert("Location service Denied", "Can not continue,please go to setting to open location service and try again.", "OK");
Xamarin.Forms.DependencyService.Get<IGpsDependencyService>().OpenSettings();
}
else
{
try
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Location);
if (status != PermissionStatus.Granted)
{
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Location))
{
await DisplayAlert("Need location", "Gunna need that location", "OK");
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Location);
status = results[Permission.Location];
}
if (status == PermissionStatus.Granted)
{
var results = await CrossGeolocator.Current.GetPositionAsync(TimeSpan.FromSeconds(10));
LabelGeolocation.Text = "Lat: " + results.Latitude + " Long: " + results.Longitude;
}
else if (status != PermissionStatus.Unknown)
{
await DisplayAlert("Location Denied", "Can not continue, try again.", "OK");
}
}
catch (Exception ex)
{
LabelGeolocation.Text = "Error: " + ex;
}
}
}
About checking GPS location on ios platform, you can take a look:
https://github.com/xamarin/Essentials/issues/1257

Xamarin Forms: How to implement location share feature?

Whenever I am opening my app I need to check the location is on or off. If the location is off, I need to show an alert to the user to enable location share like the below screenshot:
I try this using the dependency service from this thread:
The interface on shared Project:
public interface ILocSettings
{
void OpenSettings();
}
Android implementation
[assembly: Dependency(typeof(LocationShare))]
namespace ProjectName.Droid
{
public class LocationShare : ILocSettings
{
public void OpenSettings()
{
//code1
Android.App.Application.Context.StartActivity(new Android.Content.Intent(Android.Provider.Settings.ActionLocat‌​ionSourceSettings));
//code2
//LocationManager LM = (LocationManager)Android.App.Application.Context.GetSystemService(Context.LocationService);
//if (LM.IsProviderEnabled(LocationManager.GpsProvider) == false)
//{
// Context ctx = Android.App.Application.Context;
// ctx.StartActivity(new Intent(Android.Provider.Settings.ActionLocationSourceSettings));
//}
}
}
}
Finally from the shared project called like below:
var myAction = await DisplayAlert("Location", "Please Turn On Location", "OK", "CANCEL");
if (myAction)
{
if (Device.RuntimePlatform == global::Xamarin.Forms.Device.Android)
{
DependencyService.Get<ILocSettings>().OpenSettings();
}
}
else
{
await DisplayAlert("Alert", "User Denied Permission", "OK");
}
I am getting below exception when running this. (Getting the same exception for code1 and code2)
System.NullReferenceException: 'Object reference not set to an instance of an object.'
I need to show the alert only if the location is off. If the location is on, no need to do these things. How I can check the location is on or off?
Also, I need to implement the same feature for ios and windows platforms.
Update 1
Hi #Lucas Zhang - MSFT
I have tried your solution and got an alert like this. But after giving the location access, still the device's location is off. I need to on the device's location like this when the user taps the OK option in the alert (question screenshot). Either on the location directly or redirect to the location settings page.
Update 2
Tried GeolocatorPlugin and used the below code for checking the GPS is off or on. Always getting False value even if the GPS is on.
public bool IsLocationAvailable()
{
if (!CrossGeolocator.IsSupported)
return false;
return CrossGeolocator.Current.IsGeolocationAvailable;
}
Made below modification on the android service and now I am able to open the location settings.
public class LocationShare : ILocSettings
{
public void OpenSettings()
{
Intent intent = new Android.Content.Intent(Android.Provider.Settings.ActionLocat‌​ionSourceSettings);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
Before opening the location settings page, I need to verify the GPS is on or off (not the location permission).
Also I didn't understand the ios answer by Jack. So can you show me the ios dependency service like I did for android for opening ios location settings page?
In your case you could use the plugin PermissionsPlugin from Nuget.
Usage
try
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync<LocationPermission>();
if (status != PermissionStatus.Granted)
{
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Location))
{
await DisplayAlert("Need location", "Gunna need that location", "OK");
}
status = await CrossPermissions.Current.RequestPermissionAsync<LocationPermission>();
}
if (status == PermissionStatus.Granted)
{
//Query permission
}
else if (status != PermissionStatus.Unknown)
{
//location denied
}
}
catch (Exception ex)
{
//Something went wrong
}
Update
It seems that you want to check if system location is open or not , right ? If so , you could try to achieve GPS info after you get the location permission . If the GPS info is still unavailable , that means the system setting is OFF .And you can invoke dependency service to open platform setting page.
public async void ShareLocation()
{
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
if (status == PermissionStatus.Granted)
{
bool gpsStatus = DependencyService.Get<ILocSettings>().isGpsAvailable();
if (!gpsStatus)
{
var myAction = await DisplayAlert("Location", "Please turn on GPS for the proper working of the application.", "TURN ON", "CANCEL");
if (myAction)
{
DependencyService.Get<ILocSettings>().OpenSettings();
}
}
}
}
//ILocSettings
public interface ILocSettings
{
void OpenSettings();
bool isGpsAvailable();
}
//Android Dependency Service
[assembly: Dependency(typeof(LocationShare))]
namespace Projectname.Droid.Services
{
public class LocationShare : ILocSettings
{
public bool isGpsAvailable()
{
bool value = false;
Android.Locations.LocationManager manager = (Android.Locations.LocationManager)Android.App.Application.Context.GetSystemService(Android.Content.Context.LocationService);
if (!manager.IsProviderEnabled(Android.Locations.LocationManager.GpsProvider))
{
//gps disable
value = false;
}
else
{
//Gps enable
value = true;
}
return value;
}
public void OpenSettings()
{
Intent intent = new Android.Content.Intent(Android.Provider.Settings.ActionLocat‌​ionSourceSettings);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
}
For iOS
public void CheckAuthorization(CLLocationManager manager, CLAuthorizationStatus status)
{
switch (status)
{
case CLAuthorizationStatus.Authorized | CLAuthorizationStatus.AuthorizedAlways | CLAuthorizationStatus.AuthorizedWhenInUse:
Console.WriteLine("Access");
break;
case CLAuthorizationStatus.Denied | CLAuthorizationStatus.NotDetermined | CLAuthorizationStatus.Restricted:
Console.WriteLine("No Access");
break;
default:
Console.WriteLine("No Access");
break;
}
}
UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));

Xamarin.Forms and Plugin.Media: after about 20 photos something crashes

I have a problem with Xamarin.Forms ver. 2.3.4.224 and Plugin.Media ver. 2.6.2. The problem occurs after taking about 20 photos (depends from the device): basically the app crashes without any apparently reason.
If you want to replicate the error, I created a test project for you on GitHub. With my iPad Air or iPad Pro after about 30 photos (video iPad Air - iPad Pro). All devices are iOS ver. 10.3.1 and they have enough space to storage photos.
The app is very simple: you have two buttons one for taking a picture and the other one to pick a photo. If you take photos one after another, after about 20 (32 in an iPad Air) the app crashes. I'm just take photos with the Plugin.Media nothing more.
Any ideas are welcome.
Update
In my project I had a reference to Refractored.MvvmHelpers and I noticed if I remove it, I can take more pictures. I created my BaseViewModel with INotifyPropertyChanged and I noticed I can take more photos.
I created then a new project (you can find it on GitHub under cameratesteasy) without MVVM and there is just the code to take a photo like:
public partial class cameratesteasyPage : ContentPage
{
int count = 0;
public cameratesteasyPage()
{
InitializeComponent();
CrossMedia.Current.Initialize();
}
void UpdateCount()
{
count++;
CountLabel.Text = $"{count} times";
}
async void StartCameraTapped(object sender, System.EventArgs args)
{
using (var file = await CrossMedia.Current.TakePhotoAsync(
new StoreCameraMediaOptions {}))
{
if (file == null)
return;
UpdateCount();
}
}
async void StartCameraTakeTapped(object sender, System.EventArgs args)
{
var file = await CrossMedia.Current.PickPhotoAsync();
if (file == null)
return;
UpdateCount();
}
}
In this case the app shut down after 52 photos. I saved the log for Xcode and you can see it here.
I used Xamarin Profile and the memory level is always low. After about 30 photos, an error occurs in Xamarin Profiler
Finally I could create a Xamarin Profiler file
Also I noticed this kind of error occurs on iPads. The same app in an iPhone is working fine (apparently) or I didn't find up to now the number of photos before crashing.
Update /2
I decided to implement a native function for taking photo.
Interface
public interface ICamera
{
void TakePicture();
}
Implementation
using System;
using cameratest.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
[assembly: Xamarin.Forms.Dependency(typeof(Camera_iOS))]
namespace cameratest.iOS
{
public class Camera_iOS : ICamera
{
static UIImagePickerController picker;
static Action<NSDictionary> _callback;
static void Init()
{
if (picker != null)
return;
picker = new UIImagePickerController();
picker.Delegate = new CameraDelegate();
}
class CameraDelegate : UIImagePickerControllerDelegate
{
public override void FinishedPickingMedia(
UIImagePickerController picker, NSDictionary info)
{
var cb = _callback;
_callback = null;
picker.DismissModalViewController(true);
cb(info);
}
}
public static void TakePicture(UIViewController parent,
Action<NSDictionary> callback)
{
Init();
picker.SourceType = UIImagePickerControllerSourceType.Camera;
_callback = callback;
parent.PresentModalViewController(picker, true);
}
public static void SelectPicture(UIViewController parent,
Action<NSDictionary> callback)
{
Init();
picker.SourceType = UIImagePickerControllerSourceType.PhotoLibrary;
_callback = callback;
parent.PresentModalViewController(picker, true);
}
public void TakePicture()
{
var rc = UIApplication.SharedApplication.KeyWindow.RootViewController;
TakePicture(rc, (obj) =>
{
var photo = obj.ValueForKey(
new NSString("UIImagePickerControllerOriginalImage")) as UIImage;
var documentsDirectory =
Environment.GetFolderPath(Environment.SpecialFolder.Personal);
// hardcoded filename, overwritten each time
string jpgFilename = System.IO.Path.Combine(documentsDirectory,
"Photo.jpg");
NSData imgData = photo.AsJPEG();
NSError err = null;
if (imgData.Save(jpgFilename, false, out err))
{
Console.WriteLine("saved as " + jpgFilename);
}
else
{
Console.WriteLine("NOT saved as " +
jpgFilename + " because" + err.LocalizedDescription);
}
});
}
}
}
With this code after about 30 photos, the app crashes. The only difference is with this code I can receive some alert from ReceiveMemoryWarning. If you have an interest, I updated the code on GitHub.

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
}

Resources