Xamarin.Forms Popup Page from background service - c#

I am working with Xamarin.Forms on a project which is required to have a background service that will lookup for a date to remind the user of some important activity, but has to behave like an alarm and to be like a popup page or some sort of where I can make some XAML and bind some pieces of information from SQLite.
I have made a service that works in the background, I have made a subscription via MessagingCenter, and all is working so well the only thing I can't do is just from the background to simply open designed page bindings and VM will do the rest of the job.
I spent 5 days searching around but nothing seems to work...
Help will be well appreciated.

I have found a different solution to my problem. using AlarmManager here is a very simple code that will just start the new activity I have just created a new constructor in app.xaml.cs that will open the reminder page as the main page. and LoadApp will be called from ReminderActivity but with
LoadApplication(new App(true));
void LoadAlarm()
{
var alarmIntent = new Intent(this, typeof(AlarmReceiver));
var pending = PendingIntent.GetBroadcast(this, 0, alarmIntent, PendingIntentFlags.UpdateCurrent);
var alarmManager = GetSystemService(AlarmService).JavaCast<AlarmManager>();
alarmManager.Set(AlarmType.ElapsedRealtime, SystemClock.ElapsedRealtime() + 600 * 1000, pending);
}
and
[BroadcastReceiver]
public class AlarmReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
intent = new Intent(Android.App.Application.Context, typeof(ReminderActivity));
intent.AddFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
Android.App.Application.Context.StartActivity(intent);
}
}

Related

How to get notifications to appear right after page loads? (Xamarin, LocalNotifications Plugin)

I have a project that needs notifications about upcoming events to show when the user opens the main page, and I'm having problems with async and await (I think). I am using the LocalNotifications Nuget plugin, because that's the only one I was allowed to use for this project. I have some methods that are intended to check for and display notifications. I have tried loading them from my App file, My MainPage.Xaml.cs's OnAppearing method, and from my MainPageViewModel.cs, and the closest that I've gotten to something working is with a combination of creating the Notifications as a class, adding them from the ViewModel and displaying them in the OnAppearing Method of the code behind. The code works right up until the if statement's body in the OnAppearing Method tries to execute and then it just stops and continues on with displaying the main page, but never showing the notifications. I am really new to using asynchronous programming, as well as Xamarin, so I'm pretty confused. This is my relevant code.
//Code starts in MainPageViewModel.cs
public MainPageViewModel()
{
Task.Run(async() => await CheckNotifications());
}
private async Task CheckNotifications()
{
await DatabaseService.Init();
var eventList = await DatabaseService.db.Table<Event>().ToListAsync();
if (eventList.Count > 0)
{
foreach (Event event in eventList)
{
if (event.NotifyStartDate && (event.StartDate.Date == DateTime.Today))
{
Notifications notification = new Notifications
{
Name = $"{event.Name}",
NotifyDate = event.StartDate,
Occurrence = "Starting"
};
await DatabaseService.AddNotification(notification);
}
if (event.NotifyEndDate && (event.EndDate.Date == DateTime.Today))
{
Notifications notification = new Notifications
{
Name = $"{event.Name}",
NotifyDate = event.EndDate,
Occurrence = "Ending"
};
await DatabaseService.AddNotification(notification);
}
}
}
}
This all seems to work fine, it creates a Notification with the event that starts today and adds it to the Notifications Table in the database. Next it moves to my MainPage Code Behind's OnAppearing method--> which is an async void method and I can't figure out any other way to do this, because I get an error that "MainPage.OnAppearing() return type must be void" if I try to make it a Task.
protected override async void OnAppearing()
{
//List fills fine here and holds my Test Event starting today information with a count of 1
var notifications = await DatabaseService.db.Table<Notifications>().ToListAsync();
if (notifications.Count > 0)
{
foreach(Notifications notification in notifications)
{
CrossLocalNotifications.Current.Show("Reminder", $"Event {notification.Name} is {notification.Occurrence} Today" +
$"on {notification.NotifyDate}");
//code stops at this line and continues the rest of the program without executing notification
}
}
base.OnAppearing();
}
My Init, FillSampleData, and AddNotifications methods in the database are all async tasks, and they all await other methods, but the problem seems to occur at OnAppearing. To add to that I am Navigating to the Main Page from App.Xaml.cs Synchrounously, because when I tried to make it an asynchronous method it got ignored there.
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
Everywhere else in my app the navigation awaits. I don't know if I'm just doing the notifications all wrong, if I'm just doing Asynchronous programming all wrong, or a little bit of both. Any help would be much appreciated. Thanks guys.

Xamarin Android Activity

I am able to press the back button to from LAYOUT2 back to LAYOUT1.
But how would I go from LAYOUT3 to LAYOUT2 to LAYOUT1?
I have tried doing different things such as this
In the MainActivity I have this
public override void OnBackPressed()
{
var intent = new Intent(this, typeof(MainActivity));
StartActivity(intent);
}
In the SecondActivity I have this
public override void OnBackPressed()
{
var intent = new Intent(this, typeof(Menu));
StartActivity(intent);
}
With that code above it just stays on the SecondActivity and doesn't go back to the MainActivity - why?
By default app behaviour is simply go to last activity.
you can try without any extra coding.
I think nothing will be needed.
Remove your overrides for the OnBackPressed and the behaviour you're expecting will be achieved.

How do I get PendingIntent using the MvvmCross 5 IMvxNavigationService?

I have a method I used in MvvmCross 4.x that was used with the NotificationCompat.Builder to set a PendingIntent of a notification to display a ViewModel when the notification is clicked by the user. I'm trying to convert this method to use the MvvmCross 5.x IMvxNavigationService but can't see how to setup the presentation parameters, and get a PendingIntent using the new navigation API.
private PendingIntent RouteNotificationViewModelPendingIntent(int controlNumber, RouteNotificationContext notificationContext, string stopType)
{
var request = MvxViewModelRequest<RouteNotificationViewModel>.GetDefaultRequest();
request.ParameterValues = new Dictionary<string, string>
{
{ "controlNumber", controlNumber.ToString() },
{ "notificationContext", notificationContext.ToString() },
{ "stopType", stopType }
};
var translator = Mvx.Resolve<IMvxAndroidViewModelRequestTranslator>();
var intent = translator.GetIntentFor(request);
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
return PendingIntent.GetActivity(Application.Context,
_notificationId,
intent,
PendingIntentFlags.UpdateCurrent);
}
The RouteNotificationViewModel does appear when I click the notification but Prepare and Initialize are not being called. What is necessary to convert this method from MvvmCross 4.x style of navigation to MvvmCross 5.x style of navigation?
It's possible to do this in MvvmCross 5+ but it's not as clean as it previously was.
For starters you want to specify the singleTop launch mode for your activity:
[Activity(LaunchMode = LaunchMode.SingleTop, ...)]
public class MainActivity : MvxAppCompatActivity
Generate the notification PendingIntent like this:
var intent = new Intent(Context, typeof(MainActivity));
intent.AddFlags(ActivityFlags.SingleTop);
// Putting an extra in the Intent to pass data to the MainActivity
intent.PutExtra("from_notification", true);
var pendingIntent = PendingIntent.GetActivity(Context, notificationId, intent, 0);
Now there are two places to handle this Intent from MainActivity while still allowing the use of MvvmCross navigation service:
If the app was not running while the notification was clicked then OnCreate will be called.
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
if (bundle == null && Intent.HasExtra("from_notification"))
{
// The notification was clicked while the app was not running.
// Calling MvxNavigationService multiple times in a row here won't always work as expected. Use a Task.Delay(), Handler.Post(), or even an MvvmCross custom presentation hint to make it work as needed.
}
}
If the app was running while the notification was clicked then OnNewIntent will be called.
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
if (intent.HasExtra("from_notification"))
{
// The notification was clicked while the app was already running.
// Back stack is already setup.
// Show a new fragment using MvxNavigationService.
}
}

Xamarin.Android app resumed by shortcut intent - Master and Detail must be set before adding MasterDetailPage to a container

I have Xamarin.Android todo list mobile app using Prism.
The problem is:
In android system, I can create shortcut to open specific list in
app.
When I open app, and press home button, it remains on background
(thats ok)
When I then run app from desktop shortcut, it opens
android activity and when I create new PrismApplication (
LoadApplication(new App()); ) everything is running OK, but after
creating viewmodel for view, app is still using old viewmodel from
before.
I made this workaroud and I use same instance of PrismApplication:
static App xamApp;
protected override void OnCreate(Bundle bundle)
{
if (xamApp == null)
{
Forms.Init(this, bundle);
xamApp = new App();
}
LoadApplication(xamApp);
xamApp.Redirect(Intent.GetStringExtra("ListID"));
}
Now, problem is redirecting. This code:
public void Redirect(string listId)
{
NavigationService.NavigateAsync($"MainPage/MainNavigationPage/TodoList?id={listId}", animated: false);
}
leads to the error:
System.InvalidOperationException: Master and Detail must be set before adding MasterDetailPage to a container.
Prism should take care of Binding of Detail in MasterDetailPage by the "TodoList" from NavigateAsync uri.
Does enyone know what can be the problem here?
So I finally got it working.
First I used LaunchMode = LaunchMode.SingleTask in my ActivityAttribute of MainActivity
[Activity(Label = "..", LaunchMode = LaunchMode.SingleTask, Icon = "#drawable/icon", Theme = "#style/MainTheme", MainLauncher = true]
public class MainActivity : FormsAppCompatActivity
Then I used OnNewIntent method of FormsAppCompatActivity so after app is on backgroud, only this event is launched :
protected override void OnNewIntent(Intent intent)
{
var listId = intent.GetStringExtra("ListID");
((App)App.Current).Redirect(listId);
}
Now even $"MainNavigationPage/TodoList?id={listId}" works
Based on the info you provided, I am assuming that when the app is launched again, it is already running, your previous MasterDetail page is already on the stack. IN your reset method, you want to reset your navigation stack to the new uri passing in the parameter. IN this case, you should use an absolute uri. This means try adding a "/" prefix to your uri. So something like this:
public void Redirect(string listId)
{
NavigationService.NavigateAsync($"/MainPage/MainNavigationPage/TodoList?id={listId}", animated: false);
}

Start activity from constructor of other activity?

I'm pretty new to xamarin / android development but this is pretty much what I'm trying conceptually:
Have a main activity that checks whether or not a user is already logged in
If the user is logged in, start the MainAppActivity
If the user is not logged in, start the OnboardingActivity
The reason I think these should be in different activities is for general design reasons (separation of concerns etc) - but also because both activities have different themes. (The Onboarding process has no title bar)
In code, this should be pretty much what I need:
[Activity(Label = "MyApp", MainLauncher = true, Icon = "#drawable/icon", Theme = "#android:style/Theme.Black.NoTitleBar")]
public class MainActivity : Activity
{
private readonly TransactionService _transactionService;
public MainActivity()
{
var isLoggedIn = true; // This will be loaded from somewhere
if(isLoggedIn)
{
var intent = new Intent(this, typeof(MainAppActivity));
StartActivity(intent);
}
else
{
var intent = new Intent(this, typeof(OnboardingActivity));
StartActivity(intent);
}
}
}
However, this code just throws an unspecified exception.
If I try the same thing during the protected override void OnCreate(Bundle bundle) process, I get an exception as well.
However, when I bind something like that to a button event and click it manually, it will work. (That is not my intent, but to test there is nothing wrong with my activities)
So, question is, how do I do something like this?
Edit: Here is the stacktrace if I try it in the constructor:
0x29 in System.Diagnostics.Debugger.Mono_UnhandledException_internal C#
0x1 in System.Diagnostics.Debugger.Mono_UnhandledException at /Users/builder/data/lanes/1978/f98871a9/source/mono/mcs/class/corlib/System.Diagnostics/Debugger.cs:122,4 C#
0x6 in Android.Runtime.UncaughtExceptionHandler.UncaughtException at /Users/builder/data/lanes/1978/f98871a9/source/monodroid/src/Mono.Android/src/Runtime/UncaughtExceptionHandler.cs:35,4 C#
0x1C in Java.Lang.Thread.IUncaughtExceptionHandlerInvoker.n_UncaughtException_Ljava_lang_Thread_Ljava_lang_Throwable_ at /Users/builder/data/lanes/1978/f98871a9/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Java.Lang.Thread.cs:221,5 C#
0x1D in object.b8bd1d31-3e2e-454d-bd94-9d5dea40eddb C#
I have not used xamarin, so I could be wrong.
FYR, on native android, you should do that in the onCreate of your MainActivity instead of Constructor

Categories