I am trying to launch an explicit page whenever user clicks on an push notification to demonstrate a concept.
This code used to work earlier(last year) but i dont remember what i changed in the interim that it stopped working.
Here is the code for app.xaml .
private async void AcquirePushChannel()
{
try
{
CurrentChannel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
}
catch (Exception)
{
new MessageDialog("Cannot Contact Azure Server", "Operation Timed Out");
}
IMobileServiceTable<Registrations> registrationsTable = App.MobileService.GetTable<Registrations>();
var registration = new Registrations { Handle = CurrentChannel.Uri };
await registrationsTable.InsertAsync(registration);
}
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used when the application is launched to open a specific file, to display
/// search results, and so forth.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected async override void OnLaunched(LaunchActivatedEventArgs args)
{
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
if (!rootFrame.Navigate(typeof(MainPage), args.Arguments))
{
throw new Exception("Failed to create initial page");
}
}
string launchstring = args.Arguments;
if (!string.IsNullOrEmpty(launchstring))
{
rootFrame.Navigate(typeof(InstructionSetBlue));
}
AcquirePushChannel();
// Ensure the current window is active
Window.Current.Activate();
}
This piece of code launches the specific page.
if (!string.IsNullOrEmpty(launchstring))
{
rootFrame.Navigate(typeof(InstructionSetBlue));
}
Related
I have a Xamarin IOS application that get's the users location each 10 sec, even when the app is killed. I make us of this library: "https://jamesmontemagno.github.io/GeolocatorPlugin/".
What I want is: When the app is closed or open and the user is at a specific location, I want to show a local notification. Is that even possible when the app is closed? I can't find information on this because it's always about remote notifications.
Notification permission should be requested as soon as the app launches by adding the following code to the FinishedLaunching method of the AppDelegate and setting the desired notification type (UNAuthorizationOptions):
...
using UserNotifications;
...
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
....
//after iOS 10
if(UIDevice.CurrentDevice.CheckSystemVersion(10,0))
{
UNUserNotificationCenter center = UNUserNotificationCenter.Current;
center.RequestAuthorization(UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.UNAuthorizationOptions.Badge, (bool arg1, NSError arg2) =>
{
});
center.Delegate = new NotificationDelegate();
}
else if(UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var settings = UIUserNotificationSettings.GetSettingsForTypes(UIUserNotificationType.Alert| UIUserNotificationType.Badge| UIUserNotificationType.Sound,new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
return true;
}
New to iOS 10, an app can handle Notifications differently when it is in the foreground and a Notification is triggered. By providing a UNUserNotificationCenterDelegate and implementing the UserNotificationCentermethod, the app can take over responsibility for displaying the Notification. For example:
using System;
using ObjCRuntime;
using UserNotifications;
namespace workplat
{
public class NotificationDelegate:UNUserNotificationCenterDelegate
{
public NotificationDelegate()
{
}
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
// Do something with the notification
Console.WriteLine("Active Notification: {0}", notification);
// Tell system to display the notification anyway or use
// `None` to say we have handled the display locally.
completionHandler(UNNotificationPresentationOptions.Alert|UNNotificationPresentationOptions.Sound);
}
public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
// Take action based on Action ID
switch (response.ActionIdentifier)
{
case "reply":
// Do something
break;
default:
// Take action based on identifier
if (response.IsDefaultAction)
{
// Handle default action...
}
else if (response.IsDismissAction)
{
// Handle dismiss action
}
break;
}
// Inform caller it has been handled
completionHandler();
}
}
}
To create and register a Custom Action with the system, use the following code:
public void RegisterNotification(long time)
{
UNUserNotificationCenter center = UNUserNotificationCenter.Current;
//creat a UNMutableNotificationContent which contains your notification content
UNMutableNotificationContent notificationContent = new UNMutableNotificationContent();
notificationContent.Title = "xxx";
notificationContent.Body= "xxxx";
notificationContent.Sound = UNNotificationSound.Default;
UNTimeIntervalNotificationTrigger trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(time, false);
UNNotificationRequest request = UNNotificationRequest.FromIdentifier("FiveSecond", notificationContent, trigger);
center.AddNotificationRequest(request,(NSError obj) =>
{
});
}
When you call this method ,for emample:
RegisterNotification(20);//set the time you want to push notification
The notification will been pushed after 20 seconds,enen if you close your app. You could put this line after uploading the location .
I have upload my demo to my github, you can download it for your reference: Demo Link .
And you can access the link for more information and details: MicroSoft Document
I need to run some address validation on Customer Location addresses using a 3rd party API to determine if the address is residential or commercial. This validation should run whenever an address field is changed. In other words, the validation should be run in the Address_RowUpdated event handler.
Because the function is calling a 3rd party API, I believe that it should be done in a separate thread, using PXLongOperation so that it does not hold up address saving and fails gracefully if the API is unavailable or returns an error.
However, I am not sure if the architecture of running a long operation within an event handler is supported or if a different approach would be better.
Here is my code.
public class CustomerLocationMaint_Extension : PXGraphExtension<CustomerLocationMaint>
{
protected virtual void Address_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
PX.Objects.CR.Address row = (PX.Objects.CR.Address)e.Row;
if (row != null)
{
Location location = this.Base.Location.Current;
PXCache locationCache = Base.LocationCurrent.Cache;
PXLongOperation.StartOperation(Base, delegate
{
RunCheckResidential(location, locationCache);
});
this.Base.LocationCurrent.Cache.IsDirty = true;
}
}
protected void RunCheckResidential(Location location, PXCache locationCache)
{
string messages = "";
PX.Objects.CR.Address defAddress = PXSelect<PX.Objects.CR.Address,
Where<PX.Objects.CR.Address.addressID, Equal<Required<Location.defAddressID>>>>.Select(Base, location.DefAddressID);
FValidator validator = new FValidator();
AddressValidationReply reply = validator.Validate(defAddress);
AddressValidationResult result = reply.AddressResults[0];
bool isResidential = location.CResedential ?? false;
if (result.Classification == FClassificationType.RESIDENTIAL)
{
isResidential = true;
} else if (result.Classification == FClassificationType.BUSINESS)
{
isResidential = false;
} else
{
messages += "Residential classification is: " + result.Classification + "\r\n";
}
location.CResedential = isResidential;
locationCache.Update(location);
Base.LocationCurrent.Update(location);
Base.Actions.PressSave();
// Display relevant messages
if (reply.HighestSeverity == NotificationSeverityType.SUCCESS)
String addressCorrection = validator.AddressCompare(result.EffectiveAddress, defAddress);
if (!string.IsNullOrEmpty(addressCorrection))
messages += addressCorrection;
}
PXSetPropertyException message = new PXSetPropertyException(messages, PXErrorLevel.Warning);
PXLongOperation.SetCustomInfo(new LocationMessageDisplay(message));
//throw new PXOperationCompletedException(messages); // Shows message if you hover over the success checkmark, but you have to hover to see it so not ideal
}
public class LocationMessageDisplay : IPXCustomInfo
{
public void Complete(PXLongRunStatus status, PXGraph graph)
{
if (status == PXLongRunStatus.Completed && graph is CustomerLocationMaint)
{
((CustomerLocationMaint)graph).RowSelected.AddHandler<Location>((sender, e) =>
{
Location location = e.Row as Location;
if (location != null)
{
sender.RaiseExceptionHandling<Location.cResedential>(location, location.CResedential, _message);
}
});
}
}
private PXSetPropertyException _message;
public LocationMessageDisplay(PXSetPropertyException message)
{
_message = message;
}
}
}
UPDATE - New Approach
As suggested, this code now calls the LongOperation within the Persist method.
protected virtual void Address_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
PX.Objects.CR.Address row = (PX.Objects.CR.Address)e.Row;
if (row != null)
{
Location location = Base.Location.Current;
LocationExt locationExt = PXCache<Location>.GetExtension<LocationExt>(location);
locationExt.UsrResidentialValidated = false;
Base.LocationCurrent.Cache.IsDirty = true;
}
}
public delegate void PersistDelegate();
[PXOverride]
public virtual void Persist(PersistDelegate baseMethod)
{
baseMethod();
var location = Base.Location.Current;
PXCache locationCache = Base.LocationCurrent.Cache;
LocationExt locationExt = PXCache<Location>.GetExtension<LocationExt>(location);
if (locationExt.UsrResidentialValidated == false)
{
PXLongOperation.StartOperation(Base, delegate
{
CheckResidential(location);
});
}
}
public void CheckResidential(Location location)
{
CustomerLocationMaint graph = PXGraph.CreateInstance<CustomerLocationMaint>();
graph.Clear();
graph.Location.Current = location;
LocationExt locationExt = location.GetExtension<LocationExt>();
locationExt.UsrResidentialValidated = true;
try
{
// Residential code using API (this will change the value of the location.CResedential field)
} catch (Exception e)
{
throw new PXOperationCompletedWithErrorException(e.Message);
}
graph.Location.Update(location);
graph.Persist();
}
PXLongOperation is meant to be used in the context of a PXAction callback. This is typically initiated by a menu item or button control, including built-in actions like Save.
It is an anti-pattern to use it anytime a value changes in the web page. It should be used only when a value is persisted (by Save action) or by another PXAction event handler. You should handle long running validation when user clicks on a button or menu item not when he changes the value.
For example, the built in Validate Address feature is run only when the user clicks on the Validate Address button and if validated requests are required it is also run in a Persist event called in the context of the Save action to cancel saving if validation fails.
This is done to ensure user expectation that a simple change in a form/grid value field doesn't incur a long validation wait time that would lead the user to believe the web page is unresponsive. When the user clicks on Save or a specific Action button it is deemed more reasonable to expect a longer wait time.
That being said, it is not recommended but possible to wrap your PXLongOperation call in a dummy Action and asynchronously click on the invisible Action button to get the long operation running in the proper context from any event handler (except Initialize):
using PX.Data;
using System.Collections;
namespace PX.Objects.SO
{
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
public PXAction<SOOrder> TestLongOperation;
[PXUIField(DisplayName = "Test Long Operation", Visible = false, Visibility = PXUIVisibility.Invisible)]
[PXButton]
public virtual IEnumerable testLongOperation(PXAdapter adapter)
{
PXLongOperation.StartOperation(Base, delegate ()
{
System.Threading.Thread.Sleep(2000);
Base.Document.Ask("Operation Done", MessageButtons.OK);
});
return adapter.Get();
}
public void SOOrder_OrderDesc_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
if (!PXLongOperation.Exists(Base.UID))
{
// Calling Action Button asynchronously so it can run in the context of a PXAction callback
Base.Actions["TestLongOperation"].PressButton();
}
}
}
}
i'm using a notification hub for an UWP application following this tutorial : Getting started with Notification Hubs for Windows Universal Platform Apps.
If i test send with a Windows 8 notification like :
<?xml version="1.0" encoding="utf-8"?>
<toast>
<visual><binding template="ToastText01">
<text id="1">Test message</text>
</binding>
</visual>
</toast>
It works and if i click on the notification, it open the app through the OnLaunched() method. But if i send a UWP specific notification like :
<toast launch="app-defined-string">
<visual>
<binding template="ToastGeneric">
<text>Microsoft Company Store</text>
<text>New Halo game is back in stock!</text>
</binding>
</visual>
<actions>
<action activationType="foreground" content="See more details" arguments="details"/>
<action activationType="background" content="Remind me later" arguments="later"/>
</actions>
</toast>
The toast works but if I click on it, it open the app and OnLaunched() never called so the app stuck on spashscreen.
Thanks in advance for your help,
Regards
you need to implement OnActivated in your app.xaml.cs
protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
}
see https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/07/08/quickstart-sending-a-local-toast-notification-and-handling-activations-from-it-windows-10/
For those who have the same problem, with Dave Smits answer : juste add OnZctivated method in App.xaml.cs file and place same content as OnLaunched method :
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
await OnLaunchedOrActivated(e);
}
protected override async void OnActivated(IActivatedEventArgs e)
{
await OnLaunchedOrActivated(e);
}
private async Task OnLaunchedOrActivated(IActivatedEventArgs e)
{
// Initialize things like registering background task before the app is loaded
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
// TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
// Handle toast activation
if (e is ToastNotificationActivatedEventArgs)
{
var toastActivationArgs = e as ToastNotificationActivatedEventArgs;
// If empty args, no specific action (just launch the app)
if (toastActivationArgs.Argument.Length == 0)
{
if (rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage));
}
// Otherwise an action is provided
else
{
// Parse the query string
// See what action is being requested
// If we're loading the app for the first time, place the main page on the back stack
// so that user can go back after they've been navigated to the specific page
if (rootFrame.BackStack.Count == 0)
rootFrame.BackStack.Add(new PageStackEntry(typeof(MainPage), null, null));
}
}
// Handle launch activation
else if (e is LaunchActivatedEventArgs)
{
var launchActivationArgs = e as LaunchActivatedEventArgs;
// If launched with arguments (not a normal primary tile/applist launch)
if (launchActivationArgs.Arguments.Length > 0)
{
// TODO: Handle arguments for cases like launching from secondary Tile, so we navigate to the correct page
throw new NotImplementedException();
}
// Otherwise if launched normally
else
{
// If we're currently not on a page, navigate to the main page
if (rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage));
}
}
else
{
// TODO: Handle other types of activation
throw new NotImplementedException();
}
// Ensure the current window is active
Window.Current.Activate();
}
Im trying to implement windows phone 8.1 notification background task.
it is implemented with one bug!
the toast notification message will appear in the action center more than once. sometimes 9times.
here is my code:
public sealed class my_bg_notifier: IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{
var deferral = taskInstance.GetDeferral();
bool status = await notificationChecker.check();
if (status)
{
populateNotification(notificationChecker.count);
}
deferral.Complete();
}
}
I tried to debug so I put a breakpoint over the line status.
and I was surprised that it is called more than once and that is why my notification will pop-up more than one time.
and the message that is showed from the debugger breakpoint clearly states that there are multiple threads doing the same job simultaneously.
so i thought to prevent running the method by more than one thread by using a boolean flag as follow:
public sealed class my_bg_notifier: IBackgroundTask
{
private static bool isNotBusy = true;
public async void Run(IBackgroundTaskInstance taskInstance)
{
if (isNotBusy)
{
isNotBusy = false;
var deferral = taskInstance.GetDeferral();
bool status = await notificationChecker.check();
if (status)
{
populateNotification(notificationChecker.count);
}
deferral.Complete();
}
isNotBusy = true;
}
}
but again that didn't work.
my question is :
why would a background task run more than once by multiple thread simultanously.
and How can I block this behavioud? should I use lock keyword?
Okkkkk!!! It was my fault. In my code i registered the background task on each app launch without checking if it is already registered.
So i used code as below to check if my task is registered then no need to register it again.
var taskRegistered = false;
var exampleTaskName = "ExampleBackgroundTask";
foreach (var task in Background.BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == exampleTaskName)
{
taskRegistered = true;
break;
}
}
Source: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh977055.aspx
I have a main Activity (OceanintelligenceActivity). In this activity I register the device for push notifications and also I registered a receiver that shows a Dialog and starts the proper Activity depending on the info sent from my server. This is the code I'm using to register the device and the receiver :
protected void gcmRegistration(){
PMApplication thisApp = PMApplication.getInstance();
AppDelegate delegate = thisApp.getAppDelegate();
final Context context = this;
// Make sure the device has the proper dependencies.
GCMRegistrar.checkDevice(this);
// Make sure the manifest was properly set - comment out this line
// while developing the app, then uncomment it when it's ready.
GCMRegistrar.checkManifest(this);
// Let's declare our receiver
registerReceiver(mHandleMessageReceiver,new IntentFilter(DISPLAY_MESSAGE_ACTION));
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
Log.d("", "Lets register for Push");
GCMRegistrar.register(this, SENDER_ID);
}else {
if(GCMRegistrar.isRegisteredOnServer(this)) {
// Skips registration.
String apnsToken = delegate.sso.getAPNSToken();
if(!apnsToken.equals(regId)){
Log.d("", "The Device RegId has changed on GCM Servers");
// We should let our servers know about this
ServerUtilities.update(regId, context);
}
} else {
Log.d("","Is not register on PM Server");
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
mRegisterTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
boolean registered = ServerUtilities.register(context, regId);
// At this point all attempts to register with the app
// server failed, so we need to unregister the device
// from GCM - the app will try to register again when
// it is restarted. Note that GCM will send an
// unregistered callback upon completion, but
// GCMIntentService.onUnregistered() will ignore it.
if (!registered) {
GCMRegistrar.unregister(context);
}
return null;
}
#Override
protected void onPostExecute(Void result) {
mRegisterTask = null;
}
};
mRegisterTask.execute(null, null, null);
}
}
}
This is how I set the receiver:
private final BroadcastReceiver mHandleMessageReceiver =
new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(EXTRA_MESSAGE);
Log.d("","BroadcastReceiver onReceive");
notificationIntent = GCMIntentService.getNotificationIntent(context);
new AlertDialog.Builder(context)
.setMessage(newMessage+". Would you like to see it right now?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Show update
startActivity(notificationIntent);
}
})
.setNegativeButton("No", null).show();
}
};
GCMIntentService.getNotificationIntent(context). This line returns the the Intent with the Activity I want to start.
Whenever there is a notification onReceive gets called but the Dialog only shows if I am on the main activity. So if the app is on a different activity, onReceive still gets called but the dialog doesn't show and therefore I can't start the proper activity.
How can I display a dialog on Currently visible activity on BroadcastReceiver?
Playing around with this one and searching on google I came across a solution. It is not the best one but it works. I still can't believe there is not an easy way to get the current context in Android. So this is what I did to manage to show the Dialog regardless of what the current activity is : I have a public static property of type Context on my singleton class(AppDelegate) and on each activity I override the onResume method and set the Context to the current activity like this AppDelegate.CURRENT_CONTEXT = this. Then on my dialog : AlertDialog.Builder(AppDelegate.CURRENT_CONTEXT).....