I have been trying to write Background Task that would show raw push notification as toast. I got push notifications working when app is running.
This is my background task class:
public sealed class BackgroundNotificationsTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
RawNotification notification = (RawNotification)taskInstance.TriggerDetails;
string content = notification.Content;
Debug.WriteLine("Background raw notification obtained!");
//SendNotification(content);
}
private void SendNotification(string text)
{
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
XmlNodeList elements = toastXml.GetElementsByTagName("text");
foreach (IXmlNode node in elements)
{
node.InnerText = text;
}
ToastNotification notification = new ToastNotification(toastXml);
ToastNotificationManager.CreateToastNotifier().Show(notification);
}
}
Then I Register In MainPage.xaml.cs
private void RegisterTasks()
{
BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
var taskRegistered = false;
var exampleTaskName = "NotificationsBackground";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == exampleTaskName)
{
taskRegistered = true;
break;
}
}
if(!taskRegistered)
{
var builder = new BackgroundTaskBuilder();
builder.Name = exampleTaskName;
builder.TaskEntryPoint = "BackgroundTasks.NotificationsBackground";
builder.SetTrigger(new Windows.ApplicationModel.Background.PushNotificationTrigger());
try
{
BackgroundTaskRegistration task = builder.Register();
Debug.WriteLine("Background Task registered.");
}
catch (Exception e)
{
Debug.WriteLine("Background Task register exception: " + e.ToString());
}
}
}
Now in appxmanifest I have set 'Lock screen notifications' to Badge, then in Declarations I have added Background Task with properies Push notification selected and entry point set as BackgroundNotificationsTask.cs
![screen][2]
Am I doing something wron or is there something that I am missing?
EDIT:
Right now when i obtain push notification the app closes... anyone know why?
There are a couple of things you're doing wrong.
1) Put your BackgroundTask in a separate project
BackgroundTask projects should be Windows Runtime Components. Also make sure that your background task resides under an accessible namespace. Do not forget to reference the background task project from your app project.
2) Register the correct class
When registering your background task, always use the fully qualified class name and not the file name:
BackgroundTasks.BackgroundNotificationsTask
This is the entry point you'll have to use in the package manifest file and in your code (given that the task class is in the project explained under 1) and the namespace is called BackgroundTasks).
3) Call RequestAccessAsync()
Make sure you call this before registering any tasks:
BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
Edit: There is a pretty good walkthrough on MSDN https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh977055.aspx
Related
Currently I'm trying to develop a solution uses .Net Core 3.1 and it's responsible to select multiple capture windows, to be shared when focused.
I've made it work using a list, but I think would be much more elegant and user friendly if I've a picker that shows a preview of the window before selecting.
I'm trying to use GraphicsCapturePicker but I'm getting the following error:
The application called an interface that was marshalled for a different thread
Here is the following methods that I'm using to open the picker:
public async void OpenCapturePicker(object sender, RoutedEventArgs e)
{
Console.WriteLine("OpenCapturePicker");
await Dispatcher.InvokeAsync(OpenCapturePicker);
}
private async void OpenCapturePicker()
{
var picker = new GraphicsCapturePicker();
picker.SetWindow(_capturableWindowHandle);
var item = await picker.PickSingleItemAsync();
if (item == null) return;
//rest of code
}
The _capturableWindowHandle is defined in the constructor of the class like:
public SomeClass(){
_capturableWindowHandle = new WindowInteropHelper(this).Handle;
}
Also I've an extension class that allows me to use the SetWindow method
public static class CaptureHelper
{
static readonly Guid GraphicsCaptureItemGuid = new Guid("79C3F95B-31F7-4EC2-A464-632EF5D30760");
public static GraphicsCaptureItem CreateItemForWindow(IntPtr windowInteropHandler)
{
var factory = WindowsRuntimeMarshal.GetActivationFactory(typeof(GraphicsCaptureItem));
var interop = (IGraphicsCaptureItemInterop) factory;
var itemPointer = interop.CreateForWindow(windowInteropHandler, GraphicsCaptureItemGuid);
var item = Marshal.GetObjectForIUnknown(itemPointer) as GraphicsCaptureItem;
Marshal.Release(itemPointer);
return item;
}
public static void SetWindow(this GraphicsCapturePicker picker, IntPtr hwnd)
{
var interop = (IInitializeWithWindow)(object)picker;
interop.Initialize(hwnd);
}
}
Update
Here is the link of the branch that I'm using to add this feature
Any ideas?
Update:
This Code worked for me:
if (!GraphicsCaptureSession.IsSupported()) {
Trace.WriteLine("No Capture Support");
return;
}
// var picker = new GraphicsCapturePicker();
// picker.SetWindow(_capturableWindowHandle);
// var item = await picker.PickSingleItemAsync();
var interopWindow = new WindowInteropHelper(this);
var hwnd = interopWindow.Handle;
var picker = new GraphicsCapturePicker();
picker.SetWindow(hwnd);
var item = await picker.PickSingleItemAsync();
if (item == null) {
return;
}
_windowCaptureService.StartCapture(item, _capturableWindowHandle);
Be aware: This code is not doing anything if you put a breakpoint in it... Maybe it is initialized with the debugger window in this case...??? But without breakpoints it is working on my Windows 10 machine.
First Answer:
The error message is telling you, that something was created on a different thread than it is used later on. I don't see your complete code here, but you should check, if you e.g. call the constructor of SomeClass in the same thread as
picker.SetWindow(_capturableWindowHandle);
Same thing with all other code involved here or prior to OpenCapturePicker...
Scratching my head on this one.
I've got a background task in my UWP application which is registered to run every 15 minutes (using TimeTrigger) and whenever the internet becomes available (using a SystemTrigger). I know for a fact that these are registered correctly as both appear in the "Lifecycle Events" when debugging using visual studio. Nevertheless, my code for registering them is below:
bool registered1 = false;
bool registered2 = false;
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == "BackgroundGPS")
{
registered1 = true;
}
if (task.Value.Name == "InternetAvailGPS")
{
registered2 = true;
}
}
await BackgroundExecutionManager.RequestAccessAsync();
if (!registered1)
{
var builder1 = new BackgroundTaskBuilder();
builder1.Name = "BackgroundGPS";
builder1.TaskEntryPoint = "BackgroundTasks.BackgroundGPSTask";
var triggerTime = new TimeTrigger(15, false);
builder1.SetTrigger(triggerTime);
BackgroundTaskRegistration task1 = builder1.Register();
}
if (!registered2)
{
var builder2 = new BackgroundTaskBuilder();
builder2.Name = "InternetAvailGPS";
builder2.TaskEntryPoint = "BackgroundTasks.BackgroundGPSTask";
var triggerIA = new SystemTrigger(SystemTriggerType.InternetAvailable, false);
builder2.SetTrigger(triggerIA);
BackgroundTaskRegistration task2 = builder2.Register();
}
I have ensured that the tasks are declared correctly in my manifest. If they weren't, my app would be throwing an exception when trying to register them.
If I run in debug mode I can see that both BackgroundGPS and InternetAvailGPS are shown in the Lifecycle Events. However, when I click on either of them to force them to execute, I get the following in the output window:
The program '[4728] backgroundTaskHost.exe' has exited with code 1 (0x1).
I have a breakpoint set at the first line of code in my 'Run' method of the background task but this is never hit. The background task is never loaded nor run, and I've no idea why. This probably isn't an issue with my Run method, but it looks like this (I've omitted much of the meat of it, and just included the beginning and end)
public async void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("GPS Started");
int errCode = 0;
try
{
_deferral = taskInstance.GetDeferral();
saveGPSStatus(DateTime.Now.ToString(), "", " ");
var access = await Geolocator.RequestAccessAsync();
if (access != GeolocationAccessStatus.Allowed)
{
Debug.WriteLine("No access");
saveGPSStatus("", "", "No GPS Access");
return;
}
Geolocator locator = new Geolocator();
locator.DesiredAccuracyInMeters = 100;
Geoposition position = await locator.GetGeopositionAsync();
//Stuff goes on in here
_deferral.Complete();
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
saveGPSStatus("", "", "Unexpected error. Err code "+errCode+". " + e.Message);
_deferral.Complete();
}
}
I have tried this both on a Windows Phone and a Windows Tablet both running build 10.0.10586.164, and they both do not execute my background task.
As I said above, these were working perfectly a few weeks ago and have only realised something was wrong as some of the app's users have been complaining!
Any help is greatly appreciated.
Got it working in the end, I was simply missing a reference to my background task project within my main project. I must have deleted it by accident at some point before that.
Just to expand on not having it referenced, not having the correct TaskEntryPoint namespace and class name will also cause this error. For me, I misspelt the class name.
I am working on integrating the geolocator plugin into my Xamarin.Forms app. I have installed the nuget package into .core, .droid & .ios. I haven't added it the the other projects in the solution, as I am on mac and they aren't supported.
I have added the example snippet (less the print to console lines), but it is throwing compiler errors. I have added using Geolocator; at the top, but the var position line throws error - the 'await' operator can only be used when its containing method is marked with the 'async' modifier - What have I done wrong?
I have included a screen shot below:
[![Compiler Errors][1]][1]
Any thoughts would be much appreciated.
UPDATE
My code now runs, I have the following structure:
namespace MyApp
{
public partial class HomePage : ContentPage
{
// Class Definitions
public HomePage(IAdapter adapter)
{
InitializeComponent();
this.adapter = adapter;
var LocManager = new CLLocationManager();
LocManager.AuthorizationChanged += (sender, args) => {
Debug.WriteLine ("Authorization changed to: {0}", args.Status);
};
if (UIDevice.CurrentDevice.CheckSystemVersion(8,0))
LocManager.RequestAlwaysAuthorization();
NewDeviceButton.Clicked += async (sender, e) => {
//Code which I would like to be able to use GetLatitude.
}
}
async Task<double> GetLongitude()
{
var locator = CrossGeolocator.Current;
locator.DesiredAccuracy = 50;
var position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
var longitude = position.Longitude;
return longitude;
}
}
However, I get the following error message.
On iOS 8.0 and higher you must set either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in your Info.plist file to enable Authorization Requests for Location updates! I originally only had the async method, but having seen the error message and reading your app note, I added the additional code at the top to try and authorise location services. However, I now get an error message saying Error CS0246: The type or namespace name 'CLLocationManager' could not be found. Are you missing an assembly reference? (CS0246) this shows on the var LocManager line. Why is this and what should I do to fix it?
It's normal an await method need to be run in an async method.
Therefore, you need to call your method in the OS projects.
Here the steps to work with geolocator plugin :
Create an interface on your xamarin form project:
public interface ILocation
{
Task<Position> GetLocation();
}
Create a class in your OS project ( Android, IOS or WP )
Here the exemple for Android
public class Location_Android : Activity, ILocation
{
public async Task<Position> GetLocation()
{
return await GetPosition();
}
private async Task<Position> GetPosition()
{
Position result = null;
try
{
var locator = CrossGeolocator.Current;
locator.DesiredAccuracy = 50;
if (locator.IsListening != true)
{
locator.StartListening(minTime: 1000, minDistance: 0);
}
var position = await locator.GetPositionAsync(10000);
//You can use Xamarin.Forms.Maps.Position
//Here I use a personnal class
result = new Position(position.Longitude, position.Latitude);
}
catch (Exception e)
{
Log.Debug("GeolocatorError", e.ToString());
}
return result;
}
}
On your class call the method like that
var position = await DependencyService.Get<ILocation>().GetLocation();
Hope this can help you.
So I'm trying to create a loading/splash screen for an app that I'm creating. Basically, if the user isn't authenticated, then they shouldn't be able to access the other parts of the app. Additionally, I'd like the app to attempt to sync the necessary database objects before it loads up the main activity.
The problem is that when I call the Authenticate() method and the InitLocalStoreAsync() methods, the screen flashes (almost like an activity reload, or like the app is doing something that I don't understand that's hiding the activity) while the methods are executing. I'd like that not to happen.
I'm very new to Android App Dev and even newer to Xamarin.
I'm using modified code that comes from the Azure Mobile Services tutorial on authentication etc.
Should I be somehow executing these methods using RunOnUiThread? If so, how do I await in conjunction with RunOnUiThread? Or should I be doing this in a completely different way?
I'm very lost. I've tried to search and find tutorials to follow, but I can't seem to find the answer. Here's the code so far:
protected override async void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Activity_Splash);
// Create your application here
try{
CurrentPlatform.Init ();
// Create the Mobile Service Client instance, using the provided
// Mobile Service URL and key
client = new MobileServiceClient (applicationURL, applicationKey);
statusText = FindViewById<TextView> (Resource.Id.SplashStatusText);
ThreadPool.QueueUserWorkItem(x => Initialize());
}catch(Java.Net.MalformedURLException){
CreateAndShowDialog (new Exception ("There was an error creating the Mobile Service. Verify the URL"), "Error");
}catch(Exception e) {
CreateAndShowDialog (e, "Error");
}
}
private async void Initialize()
{
RunOnUiThread(() => statusText.Text = "Authenticating...");
await Authenticate();
RunOnUiThread (() => statusText.Text = "Syncing...");
await InitLocalStoreAsync();
MoveToMainActivity();
}
private async Task Authenticate()
{
try
{
user = await client.LoginAsync(this, MobileServiceAuthenticationProvider.Google);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private async Task InitLocalStoreAsync()
{
// new code to initialize the SQLite store
string path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), localDbFilename);
if (!File.Exists(path))
{
File.Create(path).Dispose();
}
var store = new MobileServiceSQLiteStore(path);
store.DefineTable<ToDoItem>();
// Uses the default conflict handler, which fails on conflict
// To use a different conflict handler, pass a parameter to InitializeAsync. For more details, see http://go.microsoft.com/fwlink/?LinkId=521416
await client.SyncContext.InitializeAsync(store);
}
How do I restructure this so that I don't get any screen flashes?
If you want to run an asynchronous method you have to use the Task Factory:
RunOnUiThread(() => statusText.Text = "Loading.");
Task.Run(() => AsyncWork()).ContinueWith(result => RunOnUiThread(() => statusText.Text = "Done!"));
The screen flashes i think it could be 2 things, the app crashed and is trying to recover the last activity or your are trying to update elements on the UI thread and doing processing/work too, so it might be "stutter".
I have made a Background Taks demo. It is a 95% % copy of the SOLUTION in this Question:
Windows Phone 8.1 Background Task - Can't Debug and won't fire
The complete example OF THE SOLUTION can be download here : http://1drv.ms/1qCPLMY
The problem is when my event fires the program terminates. "my" solution can be downloaded here: http://1drv.ms/1x3z7Mp
So here is "my" code :
First the class implementing IBackgroundTask
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
namespace Tasks
{
public sealed class Upload : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine("Hello IBackgroundTask");
//return;
var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var textElements = toastXml.GetElementsByTagName("text");
var networkStateChangeEventDetails = (taskInstance.TriggerDetails as Windows.Networking.Connectivity.NetworkStateChangeEventDetails);
if (networkStateChangeEventDetails == null)
return;
textElements[0].AppendChild(toastXml.CreateTextNode("I'm message from your task!"));
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
}
}
}
And here is the code for registering the Background taks:
private async void Button_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("Registering task");
var taskRegistered = false;
var exampleTaskName = "UploadTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == exampleTaskName)
{
//taskRegistered = true;
task.Value.Unregister(true);
// break;
}
}
await BackgroundExecutionManager.RequestAccessAsync();
if (!taskRegistered)
{
Debug.WriteLine("Registering task inside");
var builder = new BackgroundTaskBuilder();
builder.Name = exampleTaskName;
builder.TaskEntryPoint = "Tasks.Upload";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.NetworkStateChange, false));
BackgroundTaskRegistration task = builder.Register();
//task.Completed += new BackgroundTaskCompletedEventHandler(NetworkStateChangeTaskOnCompleted);
//task.Trigger += new BackgroundTaskCompletedEventHandler(NetworkStateChangeTaskOnCompleted);
await new MessageDialog("Task registered!").ShowAsync();
}
}
private void NetworkStateChangeTaskOnCompleted(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs args)
{
var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var textElements = toastXml.GetElementsByTagName("text");
textElements[0].AppendChild(toastXml.CreateTextNode("NetworkStateChangeTaskOnCompleted() =>"));
textElements[0].AppendChild(toastXml.CreateTextNode("I'm message from your task!"));
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
}
I get no exception, and no error mesage. The program just terminates, when the event fires. Same on both Device and Emulator.
I've checked your project there are couple of things you need to improve:
first and most important - your BackgroundTask must be a Windows Runtime Componenet not a Class library (as it is now) - open properties of the Background Task and change it. BackgroundTask must be a runtime component - that's why your program terminates.
you will also need to change the namespace to the project's (file's) name - in this case you will have Task.Upload (instead of Tasks.Upload). Remember also to change entry in Declarations in package.appxmanifest file.
As I've tried after this changes your app should work fine.