App.xaml.cs ctor is called when OnResume() should be called - xamarin.forms

If I navigate in my App and go back to the Android LaunchScreen with the HomeButton the OnSleep() Hook is called, which is fine. If I navigate back into the App using the Android TaskManager OnResume() is called.
If I navigate in my App with the Hardware BackButton then also OnSleep() is called which is fine, but if I navigate back into my app then the OnCreate Method in the MainActivity is called which recreated the App.
global::Xamarin.Forms.Forms.Init (this, bundle);
DisplayCrashReport();
LoadApplication (new Gtue.Mobile.App ());
That should not happen. In the ctor of App.xaml I initialize stuff which only should be initialized once. I tried every LaunchMode for the MainAcitivity, nothing helped.
Is there a way to find out if the App was already initialized?

Your App class extends from Xamarin.Forms.Application and the Xamarin.Forms.Application life cycle is bind with the Activity life cycle. You could find it in the source code :
async Task OnStateChanged()
{
if (_application == null)
return;
if (_previousState == AndroidApplicationLifecycleState.OnCreate && _currentState == AndroidApplicationLifecycleState.OnStart)
_application.SendStart();
else if (_previousState == AndroidApplicationLifecycleState.OnStop && _currentState == AndroidApplicationLifecycleState.OnRestart)
_application.SendResume();
else if (_previousState == AndroidApplicationLifecycleState.OnPause && _currentState == AndroidApplicationLifecycleState.OnStop)
await _application.SendSleepAsync();
}
So you just need to care about your Activity's life cycle. When you touch the HomeButton your application don't remove the MainActivity from the task's stack and the MainActivity will be available when you re-enter the app, so it didn't execute the OnCreate method.
When you touch the HomeButton :
[0:] MainActivity OnPause
[0:] MainActivity OnStop
[0:] Forms App OnSleep
When you use the Android TaskManager open your application :
[0:] MainActivity OnRestart
[0:] Forms App OnResume
[0:] MainActivity OnResume
But when you touch the hardware BackButton, the MainActivity will be removed from the task's stack :
[0:] MainActivity OnPause
[0:] MainActivity OnStop
[0:] Forms App OnSleep
[0:] MainActivity OnDestroy
When the MainActivity is destoryed, your application will dispose resource and it including your Gtue.Mobile.App instance. And you could see the source code about OnDestroy() method :
protected override void OnDestroy()
{
PopupManager.Unsubscribe(this);
_platform?.Dispose();
// call at the end to avoid race conditions with Platform dispose
base.OnDestroy();
}
So the next time when you open your application, it is necessary to recreated the App class. Actually, the App did only be initialized once.

I came across this issue when observing my App.xaml.cs constructor was being called when I was pressing the home button on my Android device and then clicking my app.
The well detailed answer above details the correct "anticipated behaviour", but this was not the case for me.
I was getting this kind of flow when using the home button or back button -
**On Debug Start**
MainActivity:OnCreate
XF:AppCtor
XF:OnStart
MainActivity:OnResume
**Press Home Button**
MainActivity:OnPause
XF:OnSleep
**Launch App via Click on App Icon**
MainActivity:OnCreate
XF:AppCtor
Searching eventually led me to -
App restarts rather than resumes
Which in turn resulted in the following Xamarin code at the top of MainActivity OnCreate -
if (!IsTaskRoot)
{
string action = Intent.Action;
if (Intent.HasCategory(Android.Content.Intent.CategoryLauncher) &&
!string.IsNullOrEmpty(Intent.Action) &&
action == Android.Content.Intent.ActionMain)
{
Finish();
return;
}
}
Which seems to solve the initial reload at least.
I also combined this with -
LaunchMode = LaunchMode.SingleTask
To stop some double activity shenanigans when clicking on a notification in notification screen.

Related

xamarin forms android, Font asset not found sans-serif-medium when switching to AppShell

I am attempting to code an App in Xamarin Forms.
Upon startup, if the app is logged in the the user will be taken to the main shell with flyouts etc. If they are not logged in, then they will be taken to the signup/login page.
I have the following code in my App.xaml.cs
public App()
{
Instance = this;
InitializeComponent();
AssessStartupPage();
}
public void AssessStartupPage()
{
if (App.LoggedInUser == null)
{
SwitchToSignupPage_OTP();
return;
}
SwitchToShell();
}
public void SwitchToSignupPage_OTP() => SwitchMainView(new SignupPage_OTP());
public void SwitchToShell() => SwitchMainView(new AppShell());
When I attempt to switch to the shell from another Page (by calling App.Instance.SwitchToShell) the following exception is thrown:
Java.Lang.RuntimeException
Font asset not found sans-serif-medium
However if I change the AssessStartupPage() function which is called on startup to this:
public void AssessStartupPage()
{
SwitchToShell();
return;
}
The shell loads correctly at startup. However I need to be able to have the Signup page load at start if the user is not signed in and therefore need to be able to switch to the shell once signup is complete.
I have updated all the xamarin forms and xamarin essential libraries to the latest stable version but nothing seems to help.
The problem doesn't occur when I debug the app in UWP mode.

android 12, how replace the notification trampolines

Having android sdk which intercept the push notification, and has been using notification trampoline to further open the end activity. In the case of deeplink the app who uses this sdk will open the configured deeplink handler activity.
Snippet for the trampoline:
public class NotificationTrampolineReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
final PendingResult asyncResult = goAsync();
ExecutorService executor = Executors.newSingleThreadExecutor();
asycTask(executor, new Runnable() {
#Override
public void run() {
String urlStr = getStringExtra(intent, PUSH_URL);
if (urlStr != null) {
var intent2: Intent = Intent(Intent.ACTION_VIEW, Uri.parse(urlStr));
if (intent2 != null) {
intent2.addFlags(FLAG_ACTIVITY_NEW_TASK);
intent2.addFlags(FLAG_ACTIVITY_BROUGHT_TO_FRONT);
context.startActivity(intent2);
logAnalytics(intent, Message.MessageAction.OPEN);
}
}
asyncResult.finish();
}
});
}
void asycTask(ExecutorService executor, final Runnable task) {
try {
executor.execute(task);
} catch (Throwable ex) {}
}
}
The notification trampolines is not working in Android 12 anymore.
The notification trampolines is needed in the sdk to intercept the click and do something like to log analytics event; closing the notification drawer when clicking at the Action button on the notification, etc. And this sdk does not know what activities the app may configure to handle the deeplinks.
Using a dummy activity to replace the trampoline would work, but not feel right, i.e. open the activity and inside to open another one then finish this one.
When android 12 puts restriction on the notification tramoline, does it suggest a replacement for the use case like the one here? Haven't find one.
What is the suggested new solution for intercepting the push notification tap first and then open the activity?
You are better off launching an Activity directly from the Notification. The Activity could then do the analytics and figure out what Activity needs to be launched and then delegate to that. As an alternative, the launched Activity could send the broadcast Intent to your BroadcastReceiver which could do the work.
NOTE: If you launch an Activity directly from a Notification, that is not a trampoline. The trampoline occurs when you launch a Service or a BroadcastReceiver directly from the Notification and that component then launches an Activity. The idea is that the user needs to be in control of what pops onto his screen. If he taps a notification, he expects that something will appear on his screen, if your Service launches an Activity, that can happen at any time and could possibly interrupt him.

Logging out of an account with Firebase auth

I'm building an app in Kotlin, Android Studio and Firebase.
My user have a button that have a .setOnClickListener (as I show below) to logout form Firebase. But the app closes when I open the activity/fragment where the button is.
logoutButton.setOnClickListener {
val intent =
Intent(activity, AccountFragment::class.java)
Firebase.firebaseAuth.signOut()
startActivity(intent)
}
It isn't giving me any errors on Logcat...at least that I see.
When i change to the fragment that have the button, the app closes and say that the app stopped.
UPDATE: there is an error on the Logcat. It says:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
This is an image of the logcat when I changed the fragment:
Logcat new image
If the button is inside a fragment, then you have place the setOnClickListener code in onViewCreated instead of onCreateView:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
logoutButton.setOnClickListener {
val intent =
Intent(activity, AccountFragment::class.java)
Firebase.firebaseAuth.signOut()
startActivity(intent)
}
}

How to navigate to page when on resume is called in app.xaml.cs for mvvm in xamarin forms app

I have an issue with navigation. When I click home screen button of device and get back to app I get app homescreen instead of pin page. Ideally it should show pin page and its working fine with back button of device.
OnStart() method has navigationasync but the same is not working with OnResume() method.
Do I have to go to each of the Platform project cs file and add the navigation there like for Android OnRestart()/OnResume() method?
If anyone knows the solution please let me know
Most commonly when writing your Xamarin Application with Prism you will have something like:
protected override void OnInitialized()
{
NavigationService.NavigateAsync("SomePage");
}
OnInitialized is called each time the App's ctor is invoked. This is an important consideration here because this means that any time that the native platform tombstones the app in the background or otherwise refreshes the app by calling OnCreate in your MainActivity or FinishedLaunching in your AppDelegate, then OnInitialized will be invoked resetting your App's Navigation stack to SomePage.
You can however override the OnStart/OnResume in PrismApplication and use whatever business logic you need to determine where to navigate and how you might want to restore your application.
public override void OnStart()
{
NavigationService.NavigateAsync("MainPage");
}
public override void OnResume()
{
if(someCondition)
{
NavigationService.NavigateAsync("SomePage");
}
else
{
NavigationService.NavigateAsync("AnotherPage");
}
}

xamarin.forms detect ios app entering foreground from background

I am creating app in Xamarin.Forms shared proeject. I want to detect app when it comes from background
currently, I have implemented OnActivated method in AppDelegate but this method is also called when we open control center in iphone. I want my app to reconnect with my server when app comes to foreground from background and show message accordingly but this message is also shown when I swipe up and open control center.
In native Xamarin.iOS app, there is a method WillEnterForegroundNotification but this method is not available here.
Is there any method that's called only if app comes from background ?
in App.xaml.cs
protected override void OnResume()
In native Xamarin.iOS app, there is a method
WillEnterForegroundNotification but this method is not available here.
You can access WillEnterForegroundNotification method in Xamarin.forms project, just override it in AppDelegate.cs:
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
public override void WillEnterForeground(UIApplication uiApplication)
{
//handle your event here
base.WillEnterForeground(uiApplication);
}
}

Resources