Tracking changes of the pictures library on Windows 10 Mobile devices - c#

I am trying to track the changes of the pictures library because I want my app to upload new photos to to a server. To track the changes I followed the MSDN article over here https://msdn.microsoft.com/en-us/magazine/mt790201.aspx
If I run my code on my phone (Windows 10 Mobile with Fall Creators Update) it does not work if the pictures are saved to the sd-card. But if remove the sd-card and reboot my phone I can read the changes from the change-tracker. On a Desktop-PC everything works fine.
This is how I enable the background task for the change-tracker:
public async Task Register()
{
// Check if your app has access to the background
var requestStatus = await BackgroundExecutionManager.RequestAccessAsync();
if (!(requestStatus ==
BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity ||
requestStatus == BackgroundAccessStatus.AllowedSubjectToSystemPolicy ||
requestStatus ==
BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity ||
requestStatus == BackgroundAccessStatus.AlwaysAllowed))
{
Debug.WriteLine("Failed to get access to the background");
return;
}
// Build up the trigger to fire when something changes in the pictures library
var builder = new BackgroundTaskBuilder();
builder.Name = "Photo Change Trigger";
StorageLibrary picturesLib =
await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
var picturesTrigger = StorageLibraryContentChangedTrigger.Create(picturesLib);
// We are registering to be activated in OnBackgroundActivated instead of
// BackgroundTask.Run; either works, but I prefer the single-process model
builder.SetTrigger(picturesTrigger);
BackgroundTaskRegistration task = builder.Register();
}
And this is how I get the changes, which contains the code that does not work:
public async Task GetChanges()
{
StorageLibrary picturesLib =
await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
StorageLibraryChangeTracker picturesTracker = picturesLib.ChangeTracker;
picturesTracker.Enable();
StorageLibraryChangeReader changeReader = picturesTracker.GetChangeReader();
// if photos are saved on the SD-card the next line does not work
IReadOnlyList<StorageLibraryChange> changes = await changeReader.ReadBatchAsync();
}
Am I doing something wrong or is this a bug in Windows 10 Mobile? I already tried to factory reset my device or reformatting my SD-Card, but nothing worked.

As described in the official blogpost:
Calling Enable()
It’s mentioned above, but just to make sure that it is clear: Apps should call ChangeTracker.Enable() as soon as they start tracking the file system and before every enumeration of the changes. This will ensure that the change tracker is not going to miss changes to the folders included in the library.
So I would suggest to call the Enable in the Register method as well, right after the task is registered.

Related

UWP registration of background task for StorageLibraryContentChangedTrigger fails with "not found"

In my UWP desktop application, I am trying to implement a function to follow changes in the images library using a background task. I followed usage examples (like this one https://learn.microsoft.com/en-us/archive/msdn-magazine/2016/december/universal-windows-platform-file-system-monitoring-in-universal-windows-platform-apps) and my code looks like that:
StorageLibrary docLib = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
var requestStatus = await BackgroundExecutionManager.RequestAccessAsync();
if (!(
requestStatus == BackgroundAccessStatus.AllowedSubjectToSystemPolicy ||
requestStatus == BackgroundAccessStatus.AlwaysAllowed))
{
return;
}
var builder = new BackgroundTaskBuilder();
builder.Name = "Background task";
StorageLibraryContentChangedTrigger libraryTrigger = StorageLibraryContentChangedTrigger.Create(docLib);
builder.SetTrigger(libraryTrigger);
var task = builder.Register();
docLib.ChangeTracker.Enable();
My understanding is that this creates an "in-process" background task, and when the change in folder content happens the App OnBackgroundActivated() will be called, so I can process changes there.
However when I call the task registration the exception is thrown. The exception is:
System.Exception
HResult=0x80070490
Message=Element not found. (Exception from HRESULT: 0x80070490)
I don't understand what is happening. I have tried to replace the trigger with Time Zone Change trigger in the code above, and it works fine (registration is successful and OnBackgroundActivated code is executed) but the content change trigger does not. I have not found any examples of a similar error. I am using Windows 10, build 19041. The only thing that may be non-standard is that I have moved the location of my document libraries from C: drive to D: drive, but that is a standard feature of Windows OS, so I don't think it should cause a problem.

How can I use correctly the activity indicator on Xamarin.forms for Android?

I'm using this plugin for showing that the app is busy, but on Android the animation is always stuck.
For example I use it in this code:
private async Task SelectWorkOrderItemAsync(WorkOrderLista WoLista) {
if (WoLista == null) return;
// show the loading
Acr.UserDialogs.UserDialogs.Instance.ShowLoading("Loading..");
// get datas from DB
WorkOrderDettaglio WoDett = await _WorkOrderService.GetDettaglioWorkOrder(WoLista.Guid_servizio);
// this code opens another page with the datas extracted above
await NavigationService.NavigateToAsync<DettaglioWoViewModel>(WoDett, Costanti.TipoPush.Normale, WoDett.NumWoAnno);
// hide the loading
Acr.UserDialogs.UserDialogs.Instance.HideLoading();
}
This is the result:
as you can see, the loading indicator after some seconds become freezed.
This behaviors is the same if I use the default ActivityIndicator.
On IOS all works fine.
How can I correctly use it?
I don't have an Android device/simulator to test at the moment and can't reproduce on UWP, but your service call is being executed asynchronously on the main thread, your ActivityIndicator should not be blocked if you execute your service call in a worker thread.
// get datas from DB
WorkOrderDettaglio WoDett = null;
await Task.Run(async () => WoDett = await _WorkOrderService.GetDettaglioWorkOrder(WoLista.Guid_servizio));
The activity indicator must be binded (isVisibleProperty and isRunningProperty) to a bool property on your ViewModel and your ViewModel must implement
INotifyPropertyChanged
Here is a greate explanation on how to achieve that.
If an overlay is needed in this answer you can find out how to achieve that.

Windows UWP connect to BLE device after discovery

I'm using BluetoothLEAdvertisementWatcher to find nearby BLE devices and it's working well. After finding them I want to connect and read/write data via GATT. But I can't figure out how to use the API after getting the BluetoothLEAdvertisement (https://msdn.microsoft.com/de-de/library/windows/apps/windows.devices.bluetooth.genericattributeprofile).
public class Adapter
{
private readonly BluetoothLEAdvertisementWatcher _bleWatcher = new BluetoothLEAdvertisementWatcher();
public Adapter()
{
_bleWatcher.Received += BleWatcherOnReceived;
}
private void BleWatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
// how to connect?
// I know, it's the wrong place to to this, but this is just an example
}
public void StartScanningForDevices(Guid[] serviceUuids)
{
_blewatcher.advertisementfilter.advertisement.serviceuuids.clear();
foreach (var uuid in serviceuuids)
{
_blewatcher.advertisementfilter.advertisement.serviceuuids.add(uuid);
}
_blewatcher.start();
}
}
I've found Samples that are using DeviceInformation.FindAllAsync instead of BluetoothLEAdvertisementWatcher but these are not working / finding any device.
UPDATE
After digging around some time, I found the following way. But unfortunately, the pairing fails. The device is just an Arduino with a BLE shield. I can definitely connect with Android and iOS. So it must be possible with UWP somehow. :/
private void BleWatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
var dev = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
// dev.DeviceInformation.Pairing.CanPair is true
// dpr.Status is Failed
DevicePairingResult dpr = await dev.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.None);
var service = await GattDeviceService.FromIdAsync(dev.DeviceInformation.Id);
}
UPDATE #2
I'm now able to discover and pair (unstable, but ok for now), but
var service = await GattDeviceService.FromIdAsync(args.Id);
throws the following Exception
System.IO.FileNotFoundException:
The system cannot find the file specified. (Exception from HRESULT: 0x80070002)
I have no clue why.
UPDATE 04/17 - CREATORS UPDATE
Microsoft have just updated their Bluetooth APIs. We now have unpaired BLE device communication!
They have very little documentation up at the moment but here is the much simplified new structure:
BleWatcher = new BluetoothLEAdvertisementWatcher
{
ScanningMode = BluetoothLEScanningMode.Active
};
BleWatcher.Start();
BleWatcher.Received += async (w, btAdv) => {
var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress);
Debug.WriteLine($"BLEWATCHER Found: {device.name}");
// SERVICES!!
var gatt = await device.GetGattServicesAsync();
Debug.WriteLine($"{device.Name} Services: {gatt.Services.Count}, {gatt.Status}, {gatt.ProtocolError}");
// CHARACTERISTICS!!
var characs = await gatt.Services.Single(s => s.Uuid == SAMPLESERVICEUUID).GetCharacteristicsAsync();
var charac = characs.Single(c => c.Uuid == SAMPLECHARACUUID);
await charac.WriteValueAsync(SOMEDATA);
};
Much better now. As I said there is next to no documentation at the moment, I have a weird issue where my ValueChanged callback stops being called after 30 seconds or so, though that seems to be a separate scoping issue.
UPDATE 2 - SOME WEIRDNESS
After some more playing around with the new creators update there are a few more things to consider when building BLE apps.
You no longer need to run the Bluetooth stuff on the UI thread. There doesn't seem to be any permissions windows for BLE without pairing so no longer necessary to run on UI thread.
You may find that your application stops receiving updates from the device after a period of time. This is a scoping issue where objects are being disposed of that shouldn't. In the code above if you were listening to ValueChanged on the charac you may hit this issue. This is because the GattCharacteristic is disposed of before it should be, set the characteristic as a global rather than relying on it being copied in.
Disconnecting seems to be a bit broken. Quitting an app does not terminate connections. As such make sure you use the App.xml.cs OnSuspended callback to terminate your connections. Otherwise you get in a bit of a weird state where Windows seems to maintain (and keep reading!!) the BLE connection.
Well it has its quirks but it works!
OLD ANSWER
Following on from Jason's correct answer about devices needing to be paired to have their services be discovered, here is some sample code to address this:
private void SetupBluetooth()
{
Watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active };
Watcher.Received += DeviceFound;
DeviceWatcher = DeviceInformation.CreateWatcher();
DeviceWatcher.Added += DeviceAdded;
DeviceWatcher.Updated += DeviceUpdated;
StartScanning();
}
private void StartScanning()
{
Watcher.Start();
DeviceWatcher.Start();
}
private void StopScanning()
{
Watcher.Stop();
DeviceWatcher.Stop();
}
private async void DeviceFound(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs btAdv)
{
if (_devices.Contains(btAdv.Advertisement.LocalName))
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
{
Debug.WriteLine($"---------------------- {btAdv.Advertisement.LocalName} ----------------------");
Debug.WriteLine($"Advertisement Data: {btAdv.Advertisement.ServiceUuids.Count}");
var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress);
var result = await device.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.None);
Debug.WriteLine($"Pairing Result: {result.Status}");
Debug.WriteLine($"Connected Data: {device.GattServices.Count}");
});
}
}
private async void DeviceAdded(DeviceWatcher watcher, DeviceInformation device)
{
if (_devices.Contains(device.Name))
{
try
{
var service = await GattDeviceService.FromIdAsync(device.Id);
Debug.WriteLine("Opened Service!!");
}
catch
{
Debug.WriteLine("Failed to open service.");
}
}
}
private void DeviceUpdated(DeviceWatcher watcher, DeviceInformationUpdate update)
{
Debug.WriteLine($"Device updated: {update.Id}");
}
The key things to note here are:
DeviceWatcher needs both Added and Updated properties set to work.
You need to catch the exception FileNotFound which occurs when attempting to interrogate a service which is not paired, or not yet ready.
UPDATE (5/5/16): The "Element Not Found" error issue seems to only happen when the bluetooth settings screen isn't open/scanning. I don't remember that being the case before 10586.218 but I haven't checked. Obviously, not every issue is fixed in the update.
UPDATE (4/29/16): The 10586.218 windows update appears to have fixed the problem of pairing with a device that has never been paired to the machine (or phone) before. The process I've outlined here and Gerard Wilkinson's sample code in his answer should work more consistently now.
If you are lucky enough to get this to work, it requires waiting a considerable amount of time for the driver to install. I've done it by having both BluetoothLEAdvertisementWatcher and a DeviceWatcher running simultaneously.
Save the DeviceInformation from the BluetoothLEDevice that you get from FromBluetoothAddressAsync() then Dispose() the BluetoothLEDevice before initiating pairing. This is important. If you don't, it won't see the Gatt Services after pairing.
Then wait for the DeviceWatcher to see the paired device. It can take minutes but you'll usually get it before the progress bar for device installation (in the Bluetooth control panel) gets to 100%. If FromIdAsync still fails, it usually means there was a driver installation error. You can unpair and then do the pairing process over again. That usually works for me.
It's very unstable, though, and it seems to be dependent on which Bluetooth chipset and driver the machine has. I often get an Element Not Found error with FromBluetoothAddress but if it gets past there, pairing usually works on the first or second try.
PairAsync and UnpairAsync also need to be posted to the UI thread. If it isn't able to pop up a blue dialog asking for authorization, you'll get exceptions. You can use Post() from a saved UI SynchronizationContext or Windows.ApplicationModel.Core.CoreApplication.MainView.Dispatcher.RunAsync() with an async delegate to do this.
I've seen multiple posts from MS employees on the forums saying FromBluetoothAddressAsync() only works for paired devices. This isn't the case but it is buggy and seems to work best if the device has been paired manually at least once in the past.
Gerard Wilkinson's answer is correct. To make life easier, I turned it into an awaitable method using Reactive Extensions (). Any comments are welcome.
So once you found the device using the BluetoothLEAdvertisementWatcher and paired with it, you can use this to enable GATTServices.
private async Task<GattDeviceService> GetGATTServiceAsync(string deviceName)
{
//devicewatcher is abused to trigger connection
var deviceWatcher = DeviceInformation.CreateWatcher(); //trick to enable GATT
var addedSource = Observable.FromEventPattern(deviceWatcher, nameof(deviceWatcher.Added))
.Select(pattern => ((DeviceInformation)pattern.EventArgs));
var updatedSource = Observable.FromEventPattern(deviceWatcher, nameof(deviceWatcher.Updated))
.Select(pattern =>
{
var update = ((DeviceInformationUpdate)pattern.EventArgs);
return Observable.FromAsync(() => DeviceInformation.CreateFromIdAsync(update.Id).AsTask());
}).Concat();
var source = addedSource.Merge(updatedSource);
source.Publish().Connect(); //make sure the event handlers are attached before starting the device watcher
deviceWatcher.Start();
var result = await source.Where(di => di.Name == deviceName) //find the relevant device
.Select(di => Observable.FromAsync(() => GattDeviceService.FromIdAsync(di.Id).AsTask())) //get all services from the device
.Concat() //necessary because of the async method in the previous statement
.Where(service => service.Uuid == SERVICE_UUID) //get the service with the right UUID
.Retry() //GattDeviceService.FromIdAsync can throw exceptions
.FirstAsync();
deviceWatcher.Stop();
return result;
}
Basically you have the answer partly included in the questions. In essence you use the BluetoothLEAdvertisementWatcher for finding the devices only, basically they work just as beacons.
And you are not suppose to connect these devices by only using this API. To connect the devices you must use DeviceInformation.FindAllAsync(), and to get it to show you any devices, you do need to pair them first.
Anyway, if you are interested on getting data from some specific BLE characteristics, you could try using GattCharacteristicNotificationTrigger, for full example & a bit of additional explanations see my blog.

C# Windows Phone 8.1 runtime: how to make a task survive beyond app lifecycle?

I have this kind of Task
private async Task SaveToFile(StorageFile file)
{
// prepare data
await ...
Debug.Writeline("completed");
}
If the user press "back" button this task won't be completed. I need a way to make it go on until all it's done, even if the calling app is not running any more.
You have To use "background Task" for this. Its pretty simple,
create a new project for background tasks and add it to your solution, because your background task is a not a simple class its a separate WINRT project. To do this, right-click on your solution node in the Solution Explorer and select Add->New Project. Then select the Windows Run-time Component (Universal Windows) project type, name the project, and click OK. I named here "BackgroundStuff". You can create more than one background task for single application. there is no any limit.
Consider simple example to generate toast even App is not running:
private async void button_Click(object sender, RoutedEventArgs e)
{
string myTaskName = "MyBackgroundClass";
// check if task is already registered
foreach (var cur in BackgroundTaskRegistration.AllTasks)
if (cur.Value.Name == myTaskName)
{
await (new MessageDialog("Task already registered")).ShowAsync();
return;
}
// Windows Phone app must call this to use trigger types (see MSDN)
await BackgroundExecutionManager.RequestAccessAsync();
// register a new task
BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder { Name = "MyBackgroundClass", TaskEntryPoint ="MybackgroundStuff.MyBackgroundClass" };
taskBuilder.SetTrigger(new TimeTrigger(15, true));
BackgroundTaskRegistration myFirstTask = taskBuilder.Register();
If you want to learn more , refer my blog:
http://windowsapplife.blogspot.in/
No, you can not force a Task to keep running, if the parent process is terminated.
That said, the issue you've described is not really an issue unless your file takes longer than 10 seconds to save (which is an eternity, in computer lingo).
Windows Phone apps continue to run in the background for up to 10 seconds, after the user 'backs' out of it, or manually quits. This should be enough time for any application to finish its 'cleanup' operations.
See: App Lifecycle Windows Runtime Apps
See also: App activation and deactivation for Windows Phone 8

Register Background Task in Silverlight 8.1 app

I'm working on an app that uses BLE to communicate with an item and I need to receive background notifications from it. I am aware of the existence of GattCharacteristicNotificationTrigger but I can't find any way to register a Background Task in a Silverlight 8.1 app.
Any tip?
Registering a BackgroundTask is quite well explained here at MSDN.
Here is simple example fired upon TimeTrigger and showing a Toast, the steps are (applies to both - RunTime and Silverlight apps):
1. BackgroungTask must be a Windows Runtime Componenet (no matter if your App is Runtime or Silverlight). To add a new one, right click on your Solution in Solution Explorer window in VS, select Add then New project and choose Windows Runtime Component.
2. Add a reference in your main project.
3. Specify Declarations in Package.appxmanifest file - you need to add a Backgorund Task, mark Timer and specify Entry Point for the Task. The Entry Point will be a Namespace.yourTaskClass (which implements IBackgroundTask) - the added Windows Runtime Component.
4. How can your BackgroundTask look like? - let's say we want to send a Toast from it (of course it can be many other things):
namespace myTask // the Namespace of my task
{
public sealed class FirstTask : IBackgroundTask // sealed - important
{
public void Run(IBackgroundTaskInstance taskInstance)
{
// simple example with a Toast, to enable this go to manifest file
// and mark App as TastCapable - it won't work without this
// The Task will start but there will be no Toast.
ToastTemplateType toastTemplate = ToastTemplateType.ToastText02;
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
XmlNodeList textElements = toastXml.GetElementsByTagName("text");
textElements[0].AppendChild(toastXml.CreateTextNode("My first Task"));
textElements[1].AppendChild(toastXml.CreateTextNode("I'm message from your background task!"));
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
}
}
}
5. Finally, let's register our BackgroundTask in main project:
private async void Button_Click(object sender, RoutedEventArgs e)
{
// Windows Phone app must call this to use trigger types (see MSDN)
await BackgroundExecutionManager.RequestAccessAsync();
BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder { Name = "First Task", TaskEntryPoint = "myTask.FirstTask" };
taskBuilder.SetTrigger(new TimeTrigger(15, true));
BackgroundTaskRegistration myFirstTask = taskBuilder.Register();
}
Compile, run and it should work. As you can see the task should start after 15 minutes (this time can vary as OS schedules task in specific intervals, so it will fire between 15-30 minutes). But how to debug a task faster?
There is a simple way - go to Debug location toolbar and you will see a dropdown Lifecycle events, choose your task from it and it will fire (sometimes open/close dropdown to refresh it).
Here you can download my sample code - WP8.1 Silverlight App.

Categories

Resources