Xamarin Forms Lifecycle - xamarin.forms

As App.cs is provided from Xamarin Forms I do bootstrap some of the stuff like AutoMapper there. However sometimes when the app is long in the background then the App's constructor fires again when going back from background.
This makes a bit exception since initial stuff being done twice or more.
Is that normal behaviour of XamarinForms lifecycle?
public App()
{
//Its called multiple times since app is running and going back from the background
InitializeComponent();
Bootstrap(new AppContainer(),
new TranslationBootstrap(),
new MapperBootstrap(), );
Navigate();
}
EDIT:
This happens on Android
MainActivity.cs
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
Rg.Plugins.Popup.Popup.Init(this, bundle);
CrossCurrentActivity.Current.Init(this, bundle);
RequestedOrientation = ScreenOrientation.Portrait;
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
global::Xamarin.Forms.Forms.Init(this, bundle);
UserDialogs.Init(() => this);
InteractiveAlerts.Init(() => this);
LoadApplication(new App());
}
private static async void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs unobservedTaskExceptionEventArgs)
{
var newExc = new Exception("TaskSchedulerOnUnobservedTaskException", unobservedTaskExceptionEventArgs.Exception);
await LogUnhandledException(sender, newExc);
}
private static async void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var newExc = new Exception("CurrentDomainOnUnhandledException", unhandledExceptionEventArgs.ExceptionObject as Exception);
await LogUnhandledException(sender, newExc);
}
internal static async Task LogUnhandledException(object sender, Exception exception)
{
var logger = DependencyService.Get<ILogger>();
await logger.LogAsync(sender, exception.Source, exception.Message, LogType.Fatal);
}
protected override void OnResume()
{
base.OnResume();
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != Permission.Granted)
{
ActivityCompat.RequestPermissions(this,
new[] { Manifest.Permission.WriteExternalStorage, Manifest.Permission.ReadExternalStorage }, 101);
}
}

Related

Splashscreen not appearing when app is open and in background in iOS

I am using a Xamarin Forms application with Azure push notification. I need to redirect to a splash screen when my push notification is clicked. Android working fine. But in iOS a splash screen is not visible.
I tried the below example. But it's not hitting on the OnAppearing() method.
example
This is my splash screen code
public Splash(string PushNotification)
{
PushNotificationPage = PushNotification;
LoadSettings();
NavigationPage.SetHasNavigationBar(this, false);
var sub = new AbsoluteLayout {
BackgroundColor = Code.Application.Instance.CurrentReources.SplashScreenBackground
};
splashImage = new Image
{
Source = SplashImage
};
AbsoluteLayout.SetLayoutFlags(splashImage, AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(splashImage, new Rectangle(0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
sub.Children.Add(splashImage);
if (Device.RuntimePlatform == Device.Android)
{
splashImage.HeightRequest = 270;
splashImage.WidthRequest = 270;
}
this.Content = sub;
}
protected override async void OnAppearing()
{
(App.Current as App).OnResumeHandler += Handle_OnResumeHandler;
base.OnAppearing();
splashImage.Opacity = 0;
await splashImage.FadeTo(1, 3000);
Xamarin.Forms.Application.Current.MainPage = new NavigationPage(new LoginPage(PushNotificationPage));
}
void Handle_OnResumeHandler(object sender, EventArgs e)
{
Console.WriteLine("OnPauseResumeWithPage");
}
protected override void OnDisappearing()
{
(App.Current as App).OnResumeHandler -= Handle_OnResumeHandler;
base.OnDisappearing();
}
Also added below method to App.cs
protected override void OnSleep()
{
OnSleepHandler?.Invoke(null, new EventArgs());
}
protected override void OnResume()
{
OnResumeHandler?.Invoke(null, new EventArgs());
}
I don't know this is programmatically good or bad. But its work for me now. I have call OnDisappearing() method inside splash method. Its loading only if my push notification clicked. so its work for me without issue.
if(PushNotification!=null)
{
OnAppearing();
}

Set DarkMode on the first page status bar xamarin forms android

Using xamarin forms and we are adding ability to switch between Dark-Light mode. All is good however the first page of the app in android whatever I do the status bar color wont change.
I guess that in the android project I have to call SetTheme(...) before OnCreate.
Or Am I missing something here?
Question
How do you set the status bar color depending on theme? code below does not change once the android has loaded
public void SetStatusBarColor(System.Drawing.Color color, bool darkStatusBarTint)
{
var activity = Platform.CurrentActivity;
var window = activity.Window;
window?.AddFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);
window?.ClearFlags(WindowManagerFlags.TranslucentStatus);
window?.SetStatusBarColor(color.ToPlatformColor());
var flag = (StatusBarVisibility)SystemUiFlags.LightStatusBar;
if (window != null)
{
window.DecorView.SystemUiVisibility = darkStatusBarTint ? flag : 0;
}
}
Suggestions?
thanks
Try this:
private void SetStatusBarColor(System.Drawing.Color color, bool darkStatusBarTint)
{
var activity = Platform.CurrentActivity;
var window = activity.Window;
if (window != null)
{
window.AddFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);
window.ClearFlags(WindowManagerFlags.TranslucentStatus);
window.SetStatusBarColor(color.ToPlatformColor());
StatusBarVisibility flags = default;
if (darkStatusBarTint)
flags |= (StatusBarVisibility)SystemUiFlags.LightStatusBar;
else
flags &= ~(StatusBarVisibility)SystemUiFlags.LightStatusBar;
window.DecorView.SystemUiVisibility = flags;
}
}
Or
private void SetStatusBarColor(System.Drawing.Color color, bool darkStatusBarTint)
{
var activity = Platform.CurrentActivity;
var window = activity.Window;
if (window != null)
{
window.AddFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);
window.ClearFlags(WindowManagerFlags.TranslucentStatus);
window.SetStatusBarColor(color.ToPlatformColor());
window.DecorView.SystemUiVisibility = darkStatusBarTint
? (StatusBarVisibility)SystemUiFlags.LightStatusBar
: StatusBarVisibility.Visible;
}
}
Both functions work.
You can update the StatusBar color in the MainActivity.OnCreate method and also listen to the App.Current.RequestedThemeChanged event.
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
ApplyStatusBarColor(App.Current.RequestedTheme);
App.Current.RequestedThemeChanged += (s, e) => ApplyStatusBarColor(e.RequestedTheme);
}
private void ApplyStatusBarColor(Xamarin.Forms.OSAppTheme osAppTheme)
{
if (osAppTheme == Xamarin.Forms.OSAppTheme.Dark)
SetStatusBarColor(Xamarin.Forms.Color.Blue, false);
else
SetStatusBarColor(Xamarin.Forms.Color.Yellow, true);
}
}

Run a background process to change backgroundimage of another page using timer in Xamarin.forms

Hi StackOverflow Team,
I am trying to run a background process in my App. This background process should update just Background image on one of the pages in the App every 15 seconds. So far, I tried to create a timer in the App OnStart() method and update the backgroundimage of the page in the BeginInvokeOnMainThread() method but with no success. Can anyone help me with this?
My Code -
{
private static Stopwatch stopWatch = new Stopwatch();
private const int defaultTimespan = 20;
private readonly HomePage homePage;
public App()
{
InitializeComponent();
try
{
MainPage = new MainPage();
homePage = new HomePage();
}
catch(Exception ex)
{
string str = ex.Message;
}
}
protected override void OnStart()
{
if (!stopWatch.IsRunning)
{
stopWatch.Start();
}
Device.StartTimer(new TimeSpan(0, 0, 10), () =>
{
// Logic for logging out if the device is inactive for a period of time.
if (stopWatch.IsRunning && stopWatch.Elapsed.Seconds >= defaultTimespan)
{
//prepare to perform your data pull here as we have hit the 1 minute mark
// Perform your long running operations here.
Device.BeginInvokeOnMainThread(() =>
{
// If you need to do anything with your UI, you need to wrap it in this.
// homePage.BackgroundImageSource = "goldengate.jpg";
homePage.ChangeBackgroundImage();
});
stopWatch.Restart();
}
// Always return true as to keep our device timer running.
return true;
});
}
protected override void OnSleep()
{
//stopWatch.Reset();
}
protected override void OnResume()
{
//stopWatch.Start();
}
//void ChangeHomePageImage()
//{
// Navigation.PushAsync(new HomePage(appBackground));
// Navigation.RemovePage(this);
//}
}
MainPage -
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:Excercise.Views"
x:Class="Excercise.MainPage" IsPresented="False">
<MasterDetailPage.Master>
<local:MenuPage x:Name="menuPage"></local:MenuPage>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<local:HomePage x:Name="homePage"></local:HomePage>
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>
HomePage -
public partial class HomePage : ContentPage
{
private SQLiteAsyncConnection _connection;
public HomePage()
{
InitializeComponent();
// BindingContext = new HomePageViewModel();
_connection = DependencyService.Get<ISQLiteDb>().GetConnection();
loadData("");
}
public HomePage(string BackgroundimgPath)
{
InitializeComponent();
// BindingContext = new HomePageViewModel();
_connection = DependencyService.Get<ISQLiteDb>().GetConnection();
loadData(BackgroundimgPath);
}
public HomePage(string City, string LocationKey, string StateID)
{
InitializeComponent();
_connection = DependencyService.Get<ISQLiteDb>().GetConnection();
// BindingContext = new HomePageViewModel();
try
{
// Method Calls
}
catch (Exception)
{
DisplayAlert("Error", "There was an error loading this page.", "OK");
}
}
protected override void OnAppearing()
{
this.Title = App.AppTitle;
this.firstStacklayout.Margin = new Thickness(0, (Application.Current.MainPage.Height * 0.25), 0, 0);
base.OnAppearing();
}
you are creating an instance of HomePage and trying to update it, but it is NOT the same instance that is being displayed in your MasterDetail
try something like this
var md = (MasterDetailPage)MainPage;
var nav = (NavigationPage)md.DetailPage;
var home = (HomePage)nav.CurrentPage;
home.ChangeBackgroundImage();
alternately, you could use MessagingCenter to send a message to HomePage requesting that it udpate

find device orientation, not app orientation using xamarin forms

I am using xamarin forms for my app. Bascially My app need portrait mode orientation. But When i click on the button on my page, I need to find whether my device is on portrait or landscape. But my app orientation is always showing portrait mode.
MainActivity.cs:
using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Content;
using Xamarin.Forms;
using Plugin.CurrentActivity;
namespace App2.Droid
{
[Activity(Label = "App2", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
if (!isPad(this))
{
RequestedOrientation = ScreenOrientation.Portrait;
}
else
{
RequestedOrientation = ScreenOrientation.Portrait;
}
CrossCurrentActivity.Current.Activity = this;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
public static bool isPad(Context context)
{
return (context.Resources.Configuration.ScreenLayout & Android.Content.Res.ScreenLayout.SizeMask) >= Android.Content.Res.ScreenLayout.SizeLarge;
}
public override void OnConfigurationChanged(Android.Content.Res.Configuration newConfig)
{
base.OnConfigurationChanged(newConfig);
switch (newConfig.Orientation)
{
case Android.Content.Res.Orientation.Portrait:
switch (Device.Idiom)
{
case TargetIdiom.Phone:
LockRotation(Orientation.Vertical);
break;
case TargetIdiom.Tablet:
LockRotation(Orientation.Horizontal);
break;
}
break;
case Android.Content.Res.Orientation.Landscape:
switch (Device.Idiom)
{
case TargetIdiom.Phone:
LockRotation(Orientation.Vertical);
break;
case TargetIdiom.Tablet:
LockRotation(Orientation.Horizontal);
break;
}
break;
}
}
private void LockRotation(Orientation orientation)
{
switch (orientation)
{
case Orientation.Vertical:
RequestedOrientation = ScreenOrientation.Portrait;
break;
case Orientation.Horizontal:
RequestedOrientation = ScreenOrientation.Landscape;
break;
}
}
}
}
TestPage.xaml.cs:
private void btnsubmit_Clicked(object sender, EventArgs e)
{
lblText.Text = CrossDeviceOrientation.Current.CurrentOrientation.ToString();
}
But it is showing always portrait(App Orientation). But I need device orientation.
Please help me to resolve this issue
You can use Xamarin.Essentials: Device Display Information to achieve it.
Here is my code, when device change the Orientation, will push a Alert to show the device Orientation.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
DeviceDisplay.MainDisplayInfoChanged += OnMainDisplayInfoChanged;
}
private void OnMainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{
// throw new NotImplementedException();
DisplayAlert("Info", e.DisplayInfo.Orientation.ToString() + " ", "OK");
}
}
Here is running GIF.

Banner Advertisement with Xamarin.Forms

I just want to know about the banner advertisements supported with Xamarin.Forms without any patch or loophole. Is there any advertisement provider who are providing their SDKs with the Xamarin.Forms?
Thanks in advance.
There are both SDK and step-by-step examples for Google AdMob for Xamarin.Android. You are going to need the Xamarin.GooglePlaySerives.Ads nuget.
I use it to show ads in my Xamarin.Forms app published at Google Play.
Here is the sample code for the android part of your application:
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Support.V7.App;
using Android.Gms.Ads;
using Android;
namespace AdMobExample
{
[Activity (Label = "#string/app_name", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
protected AdView mAdView;
protected InterstitialAd mInterstitialAd;
protected Button mLoadInterstitialButton;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.activity_main);
mAdView = FindViewById<AdView> (Resource.Id.adView);
var adRequest = new AdRequest.Builder ().Build ();
mAdView.LoadAd (adRequest);
mInterstitialAd = new InterstitialAd (this);
mInterstitialAd.AdUnitId = GetString (Resource.String.test_interstitial_ad_unit_id);
mInterstitialAd.AdListener = new AdListener (this);
mLoadInterstitialButton = FindViewById<Button> (Resource.Id.load_interstitial_button);
mLoadInterstitialButton.SetOnClickListener (new OnClickListener (this));
}
protected void RequestNewInterstitial ()
{
var adRequest = new AdRequest.Builder ().Build ();
mInterstitialAd.LoadAd (adRequest);
}
protected void BeginSecondActivity ()
{
var intent = new Intent (this, typeof(SecondActivity));
StartActivity (intent);
}
protected override void OnPause ()
{
if (mAdView != null) {
mAdView.Pause ();
}
base.OnPause ();
}
protected override void OnResume ()
{
base.OnResume ();
if (mAdView != null) {
mAdView.Resume ();
}
if (!mInterstitialAd.IsLoaded) {
RequestNewInterstitial ();
}
}
protected override void OnDestroy ()
{
if (mAdView != null) {
mAdView.Destroy ();
}
base.OnDestroy ();
}
class AdListener : Android.Gms.Ads.AdListener
{
MainActivity that;
public AdListener (MainActivity t)
{
that = t;
}
public override void OnAdClosed ()
{
that.RequestNewInterstitial ();
that.BeginSecondActivity ();
}
}
class OnClickListener : Java.Lang.Object, View.IOnClickListener
{
MainActivity that;
public OnClickListener (MainActivity t)
{
that = t;
}
public void OnClick (View v)
{
if (that.mInterstitialAd.IsLoaded) {
that.mInterstitialAd.Show ();
} else {
that.BeginSecondActivity ();
}
}
}
}
}
There is also a ste-by-step guide for AdMob ads for Xamarin.iOS:
using Google.MobileAds;
...
const string intersitialId = "<Get your ID at google.com/ads/admob>";
Interstitial adInterstitial;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
CreateAndRequestInterstitial ();
}
public void AfterSomeTime ()
{
if (adInterstitial.IsReady)
adInterstitial.PresentFromRootViewController (navController);
}
void CreateAndRequestInterstitial ()
{
adInterstitial = new Interstitial (intersitialId);
adInterstitial.ScreenDismissed += (sender, e) => {
// Interstitial is a one time use object. That means once an interstitial is shown, HasBeenUsed
// returns true and the interstitial can't be used to load another ad.
// To request another interstitial, you'll need to create a new Interstitial object.
adInterstitial.Dispose ();
adInterstitial = null;
CreateAndRequestInterstitial ();
};
var request = Request.GetDefaultRequest ();
// Requests test ads on devices you specify. Your test device ID is printed to the console when
// an ad request is made. GADBannerView automatically returns test ads when running on a
// simulator. After you get your device ID, add it here
request.TestDevices = new [] { Request.SimulatorId.ToString () };
adInterstitial.LoadRequest (request);
}

Resources