iOS notifications sporadic (FMX) - firemonkey

Following the Embarcadero docs at this link i'm testing notifications on iOS (in FMX app built with C++). I've done the follownig:
Added #include <System.Notification.hpp> to the header file
Set FMLocalNotificationPermission to true
Dropped TNotificationCenter component on the form
Then, i put the following code in a button click:
void __fastcall TForm1::ScheduleNotificationClick(TObject *Sender)
{
if (NotificationCenter1->Supported()) {
TNotification *myNotification = NotificationCenter1->CreateNotification();
__try {
myNotification->Name = "MyNotification";
myNotification->AlertBody = "C++ for your mobile device is here!";
// Fire in 10 seconds
myNotification->FireDate = Now() + EncodeTime(0, 0, 10, 0);
// Send notification to the notification center
NotificationCenter1->ScheduleNotification(myNotification);
}
__finally {
myNotification->DisposeOf();
}
}
}
Once in a while it works...but rarely and never more than once. Most of the time it doesn't at all (repeated deleting and reinstall of app).
Next, i tried the "Present the Notification Message Immediately" code they provide:
void __fastcall TForm1::PresentNotificationClick(TObject *Sender)
{
if (NotificationCenter1->Supported()) {
TNotification *myNotification = NotificationCenter1->CreateNotification();
__try {
myNotification->Name = "MyNotification";
myNotification->AlertBody = "C++ for your mobile device is here!";
// Set Icon Badge Number (for iOS) or message number (for Android) as well
myNotification->Number = 18;
myNotification->EnableSound = False;
// Send notification to the notification center
NotificationCenter1->PresentNotification(myNotification);
}
__finally {
myNotification->DisposeOf();
}
}
}
Nothing happens at all with this code. I've tried this from scratch several times and i'm as sure as i can be that i'm coding it per their examples. I'm using 10.3 (Embarcadero® C++Builder 10.3 Version 26.0.32429.4364). I would think my code has a problem except once in blue moon it works.
My target is iPhone running 12.1.4 and i've tried building with SDK11.4 and SDK12.0, no difference. When i first run app i get the "allow or don't allow" popup and my app subsequently shows up in the Notification settings - just doesn't work.
russ
UPDATE 3-25-2019: If I run that top block of code (from a button click on iPhone) it will now run everytime - but ONLY IF i immediately kill the app after clicking. 10 seconds later it fires the notification. Why won't the notification appear if i leave my app running??

Are you sure you are calling "PresentNotificationClick" from the TButton when you click it?

Related

Prompts on top of popup windows in Xamarin and MAUI

I have discovered the limitation of displaying prompts when they are invoked from a popup window. Specifically verified with CommunityToolkit.Maui Popups.
Here's the details:
In the Map page I have this handler for the map clicked event:
void mapClicked(object sender, MapClickedEventArgs e) {
var pin = new Pin {
Label = "Here's where it is",
Location = e.Location
};
map.Pins.Add(pin);
}
I wanted to allow the user to edit the pin label when clicking on the it, like so:
pin.InfoWindowClicked += async (s, args) => {
string pinName = ((Pin)s).Label;
await DisplayPromptAsync("Enter new label", "enter new label");
};
However, this didn't work as no DisplayPrompt was shown. I tried running it in the main thread, to no avail either.
UPDATE. I've figured it out, see answer below.
The problem arises when attempting to bring up the prompt from a popup window. Evidently, one can't have a DisplayPromptAsync (or DisplayAlert for that matter) on top of a popup.
On a platform-specific level in iOS the error message reads:
Attempt to present <UIAlertController> on <Microsoft_Maui_Controls_Platform_Compatibility_ShellFlyoutRenderer> (from <Microsoft_Maui_Controls_Platform_Compatibility_ShellFlyoutRenderer>) which is already presenting <CommunityToolkit_Maui_Core_Views_MauiPopup>.

Issue rendering UIDatePicker on iPhone XS and 11plus only

I need a combined date & time picker in a Xamarin.Forms project, so I used a custom View & Renderer which works great on my iPhone 8 Plus test device. Unfortunately on an iPhone XS or 11 Pro (physical devices, both on 13.3) the picker does not render correctly - all the dates are greyed out, and time is completely invisible yet present (I can feel the 'clicks' when swiping up/down in the area where it would be). Unsurprisingly, the simulator for both those devices renders correctly. Full sample project along with screenshots from both devices on GitHub. Here's the relevant code from the renderer in case anything jumps out:
protected override void OnElementChanged(ElementChangedEventArgs<MyDatePicker> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
_datePicker.ValueChanged -= DatePickerOnValueChanged;
}
if (e.NewElement != null)
{
if (Control == null)
{
_datePicker = new UIDatePicker(new CGRect(
e.NewElement.Bounds.X,
e.NewElement.Bounds.Y,
e.NewElement.Bounds.Width,
e.NewElement.Bounds.Height
));
_datePicker.Mode = UIDatePickerMode.DateAndTime;
_datePicker.Date = (NSDate) Element.Date;
_datePicker.MaximumDate = (NSDate) Element.Date.AddDays(1);
_datePicker.MinimumDate = (NSDate) Element.Date.Subtract(TimeSpan.FromDays(5));
_datePicker.MinuteInterval = 1;
SetNativeControl(_datePicker);
}
_datePicker.ValueChanged += DatePickerOnValueChanged;
}
}
This issue was solved by the extremely knowledgable ColeX over on the Xamarin Forums. ColeX should really get the points for this, but I'm posting the answer here so that if anyone else searches for this issue on SO they will see a solution.
The root cause is dark mode on iOS when the app is not setup to render itself differently for light/dark mode. As a result the OS thinks it is rendering a date picker on a dark background, which it was not, and so it was effectively invisible. There are a few solutions mentioned, but the one that worked for me was add the following to Info.plist in the iOS project:
<key>UIUserInterfaceStyle</key>
<string>Light</string>

How to display the alert after installation of application in Xamarin forms

I want to display the alert with two options whenever the application will open at first time after completion of installation in Xamarin.Forms.
I want to display that alert one time only, not every time whenever application opens.
You can try something like this. First install Xamarin.Essentials package from nuget package manager and use Preferences to save bool data to know if app already opened or not
protected override void OnAppearing()
{
base.OnAppearing();
bool isDispalyed = Preferences.Get("isDisplayed", false);
if (isDispalyed == false)
{
//display alert, first time app starting
Device.BeginInvokeOnMainThread(async () =>
{
await DisplayAlert("Success", "Saved", "OK");
});
Preferences.Set("isDisplayed", true);
}
else
{
//From second time onward app starting
}
}

iOS delayed notification only fires if app closed (FMX)

Based on this Embarcadero example I have the following code in a TButton click in FMX app for iOS. Supposed to pop up a notification after 10 seconds.
void __fastcall TForm1::ScheduleNotificationButtonClick(TObject *Sender)
{
if (NotificationCenter1->Supported()) {
TNotification *myNotification = NotificationCenter1->CreateNotification();
__try {
myNotification->Name = "MyNotification";
myNotification->AlertBody = "C++ for your mobile device is here!";
// Fire in 10 seconds
myNotification->FireDate = Now() + EncodeTime(0, 0, 10, 0);
// Send notification to the notification center
NotificationCenter1->ScheduleNotification(myNotification);
}
__finally {
myNotification->DisposeOf();
}
}
}
When i click the button nothing happens. But, I accidentally figured out that i can make it fire by closing the app AFTER clicking the button. If i click the button and then close the app, sure enough, at 10 seconds the notification pops up. What could possibly be suppressing the notification from firing while the app is running?
p.s. This is related to my earlier post but this is unique enough i thought clearer to make separate post.

Why does SkiaSharp Touch SKTouchAction.Moved event not work?

Summary
The Touch event is never raised with the ActionType of SKTouchAction.Moved, but SKTouchAction.Pressed is raised. Why does the .Moved event never get raised?
Detail
I am attempting to create a slider in SkiaSharp. This control will need to update the thumb (the little movable circle) when the user either touches on the slider or drags. This is hosted in a Xamarin Forms app, and I have created a SKCanvasView to draw the slider and to respond to touch events.
I have successfully responded to touch events, so I know the SKCanvasView is receiving some UI events, but the SKTouchAction.Moved is never raised.
Example Code
private SKCanvasView CreateSliderControl()
{
var control = new SKCanvasView();
control.PaintSurface += HandlePaintHeightControl;
control.EnableTouchEvents = true;
control.Touch += (sender, args) =>
{
var pt = args.Location;
switch (args.ActionType)
{
case SKTouchAction.Pressed:
// 1. This code gets hit whenever I touch the canvas
control.InvalidateSurface();
break;
case SKTouchAction.Moved:
// 2. This code never gets hit, even if I push, touch, slide, etc
control.InvalidateSurface();
break;
}
};
control.InputTransparent = false;
return control;
}
As shown above #1 gets hit (I can put a breakpoint there, and my control surface is invalidated). #2 never gets hit ever, but I expect it to get hit when I slide my finger over the control.
What I've Tried
Clean/rebuild to make sure my code actually was being deployed
Upgrade from SkiaSharp 1.60.3 to 1.68.0
All the crazy finger movements I could come up with
Read through https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/touch , but that uses XAML to assign an Effect (which I'm not familiar with) and I don't see a TouchEffect class available anywhere in my code to add it to the parent Grid that contains my SkiaCanvasView.
Found a similar issue on SkiaSharp's Github.
Try telling the OS that you want to "continue receiving touch events" by setting the Handled property in the event's args to true.
private SKCanvasView CreateSliderControl()
{
var control = new SKCanvasView();
control.PaintSurface += HandlePaintHeightControl;
control.EnableTouchEvents = true;
control.Touch += (sender, args) =>
{
var pt = args.Location;
switch (args.ActionType)
{
case SKTouchAction.Pressed:
control.InvalidateSurface();
break;
case SKTouchAction.Moved:
control.InvalidateSurface();
break;
}
// Let the OS know that we want to receive more touch events
args.Handled = true;
};
control.InputTransparent = false;
return control;
}

Resources