How to resume app from remote push notification click - c#

I'm using firebase to send push notifications from a server to my app, the message looks like this:
var message = new Message()
{
Notification = new Notification()
{
Title = "Title - Test",
Body = "Body - Test",
},
Token = registrationToken
};
The notification comes through to the device no problem. All I want from when the notification is clicked, is for the app to start if the app is closed (this works) and for the app to resume if it's in the background.
When it's in the background and I click the notification, I just get a white screen. The issue seems to go away when I remove my splash screen, however that is not an ideal solution...Here is the relevant code, what am I doing wrong?
SPLASH ACTIVITY:
[Activity(Label = "MyApp",
Theme = "#style/splashscreen",
MainLauncher = true,
NoHistory = true)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
protected override void OnResume()
{
base.OnResume();
var mainActivityIntent = new Intent(this, typeof(MainActivity));
mainActivityIntent.AddFlags(ActivityFlags.SingleTop | ActivityFlags.NoAnimation);
StartActivity(mainActivityIntent);
this.Finish();
}
public override void OnBackPressed() { }
}
MAIN ACTIVITY:
[Activity(Label = "MyApp",
Theme = "#style/MainTheme",
LaunchMode = LaunchMode.SingleTop,
MainLauncher = false,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, ScreenOrientation = ScreenOrientation.FullSensor)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static MainActivity MainActivityInstance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
this.Window.SetFlags(WindowManagerFlags.KeepScreenOn, WindowManagerFlags.KeepScreenOn);
this.Window.SetSoftInputMode(SoftInput.AdjustPan);
Forms.Init(this, savedInstanceState);
MainActivityInstance = this;
LoadApplication(new App());
}

Try changing MainLauncher = true for the main activity.

Adding ActivityFlags.ClearTop to mainActivityIntent.AddFlags() in my OnResume method on
SplashActivity has seemed to have worked:
protected override void OnResume()
{
base.OnResume();
var mainActivityIntent = new Intent(this, typeof(MainActivity));
mainActivityIntent.AddFlags(ActivityFlags.NoAnimation | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
StartActivity(mainActivityIntent);
this.Finish();
}

Related

Subscribing to notifications interfaces causing crash of application Xamarin Forms Driod

I am following the guide here however when I attempt to launch the notification in Xamarin forms Andriod, I get the following.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/local-notifications
INotificationManager notificationManager;
int notificationNumber = 0;
notificationManager = DependencyService.Get<INotificationManager>();
notificationManager.NotificationReceived += (sender, eventArgs) =>
{
var evtData = (NotificationEventArgs)eventArgs;
ShowNotification(evtData.Title, evtData.Message);
};
void ShowNotification(string title, string message)
{
Device.BeginInvokeOnMainThread(() =>
{
var msg = new Label()
{
Text = $"Notification Received:\nTitle: {title}\nMessage: {message}"
};
stacklayout.Children.Add(msg);
});
}
My Main Activity
[Activity(Label = "THEHOCKEYLAB", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize, LaunchMode = LaunchMode.SingleTop )]
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());
CreateNotificationFromIntent(Intent);
Window.SetStatusBarColor(Android.Graphics.Color.Black); //here
}
protected override void OnNewIntent(Intent intent)
{
CreateNotificationFromIntent(intent);
}
void CreateNotificationFromIntent(Intent intent)
{
if (intent?.Extras != null)
{
string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);
DependencyService.Get<INotificationManager>().ReceiveNotification(title, message);
}
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
public interface INotificationManager
{
event EventHandler NotificationReceived;
void Initialize();
void SendNotification(string title, string message, DateTime? notifyTime = null);
void ReceiveNotification(string title, string message);
}
I get the following error.
System.NullReferenceException: 'Object reference not set to an
instance of an object.'
But I have had a look and cant find anything on it am using Xamarin forms version 5.0.0.2083
Its on this line am recieving the error.
notificationManager.NotificationReceived += (sender, eventArgs) =>
{
var evtData = (NotificationEventArgs)eventArgs;
ShowNotification(evtData.Title, evtData.Message);
};
Never mind I found my answer Credit to this SO. Like all Dependency's it must be registered or will always be null.
Android notification null pointer exception. This needs to be in ur main activity.cs.
DependencyService.Register<INotificationManager, AndroidNotificationManager>();

Xamarin Forms 10 seconds black screen after Splash Screen

At first I should say that I have read all similar questions and they can not solve my problem .
I have a Splash Screen in my Xamarin Forms project and check the credentials in it and then in each condition start Main Activity with different parameter toward the condition . When Splash Screen finish it's work and disappear , the app show a black screen about 10 seconds and then main page is showing
this is my Splash :
[Activity(Theme = "#style/MainTheme.Splash",
MainLauncher = true,
NoHistory = true,
ScreenOrientation = ScreenOrientation.Portrait)]
public class SplashActivity : Activity
{
static readonly string TAG = "X:" + typeof(SplashActivity).Name;
public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState)
{
base.OnCreate(savedInstanceState, persistentState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
Log.Debug(TAG, "SplashActivity.OnCreate");
}
protected override void OnResume()
{
base.OnResume();
Task startupWork = new Task(() => { SimulateStartup(); });
startupWork.Start();
}
async void SimulateStartup()
{
Some Code ...
InvokeMainActivity("SomeType");
}
private void InvokeMainActivity(string type)
{
Intent _activity = new Intent(Application.Context, typeof(MainActivity));
_activity.PutExtra("Key", type);
_activity.AddFlags(ActivityFlags.NoAnimation);
StartActivity(_activity);
}
and this is my MainActivity :
[Activity(Label = "#string/app_name",
Icon = "#drawable/icon",
Theme = "#style/MainTheme",
MainLauncher = false,
LaunchMode = LaunchMode.SingleTask,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.Locale | ConfigChanges.LayoutDirection)
]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override async void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
Xamarin.Essentials.Platform.Init(this, bundle);
Rg.Plugins.Popup.Popup.Init(this, bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
FFImageLoading.Forms.Platform.CachedImageRenderer.Init(enableFastRenderer: true);
Window.DecorView.LayoutDirection = LayoutDirection.Rtl;
string type = Intent.GetStringExtra("SomeType");
LoadApplication(new App(type));
}
}
How can I fix this ?
Thanks to #LeonLu-MSFT I add this code in style.xml without any change in MainActivity and Spalsh Screen
<item name="android:windowIsTranslucent">true</item>
I don't have Mac and ios device to test in ios
I was also facing similar problem. I fixed it by adding
<item name="android:windowIsTranslucent">true</item>
in ~\Resources\values\styles.xml.
So if you are facing similar problem add it as per below screen shot and hopefully it will also work for you:

Calendar Repeating AlarmManager

I have a small issue with Scheduled Notification on Android - Xamarin Forms Project. I want to schedule a daily notification at 17:00. It works ok on simulator but when I deploy it on a Samsung S7 Edge - Android 8.0, it notifies me at 17:00 and after I open the notification, it will keep notify every minute or so.
Here is my code, MainActivity.cs:
[Activity(Label = "SpiritMobile", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
static readonly string CHANNEL_ID = "location_notification";
internal static readonly string COUNT_KEY = "count";
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
CreateNotificationChannel();
Intent alarmIntent = new Intent(this, typeof(AlarmReceiver));
PendingIntent pending = PendingIntent.GetBroadcast(this, 0, alarmIntent, PendingIntentFlags.UpdateCurrent);
AlarmManager alarmManager = GetSystemService(AlarmService).JavaCast<AlarmManager>();
var calendar = Calendar.Instance;
calendar.TimeZone = Java.Util.TimeZone.GetTimeZone("Europe/Bucharest");
calendar.Set(CalendarField.HourOfDay, 17);
calendar.Set(CalendarField.Minute, 00);
alarmManager.SetRepeating(AlarmType.RtcWakeup, calendar.TimeInMillis, AlarmManager.IntervalDay, pending);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
ServicePointManager.ServerCertificateValidationCallback += (o, cert, chain, errors) => true;
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
return;
}
var channel = new NotificationChannel(CHANNEL_ID, "reminders", NotificationImportance.Default)
{
Description = "reminder's channel"
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
}
Then I have AlarmReceiver.cs:
[BroadcastReceiver]
public class AlarmReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
var message = "Don't forget to submit your daily rates.";
Intent backIntent = new Intent(context, typeof(MainActivity));
backIntent.SetFlags(ActivityFlags.NewTask);
var resultIntent = new Intent(context, typeof(MainActivity));
PendingIntent pending = PendingIntent.GetActivities(context, 0,
new Intent[] { backIntent, resultIntent },
PendingIntentFlags.OneShot);
var builder = new NotificationCompat.Builder(context, "location_notification")
.SetAutoCancel(true)
.SetContentTitle("Submit your daily feeling!")
.SetContentText(message)
.SetSmallIcon(Resource.Drawable.icon);
builder.SetContentIntent(pending);
var notificationManager = NotificationManagerCompat.From(context);
notificationManager.Notify(1000, builder.Build());
}
}
Could you please tell me what is wrong here? What can be the cause of this?
You are setting your repeating alarm in OnCreate method of the Activity. So every time app is launched, a new alarm is registered with the AlarmManager. As it is repeating alarm you need to set it only once.
It can be achieved by checking if your alarm is already set.

Xamarin.Forms - change device orientation based on device resolution

is there any chance to force change screen orientation based device screen resolution?
Have app for tablets, but I want to run app on mobile device too. I have a few pages, where I need to change orientation to landscape on phone, because then is my listview a litle bit messy.
Thank you for any advice and I apologize for the bad English :)
You can use MessagingCenter to achieve that .
For example , when navigating from MainPage to PageTwo in Forms , the content page of PageTwo as follow :
public partial class PageTwo : ContentPage
{
public PageTwo()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Send(this, "allowLandScape");
}
//during page close setting back to portrait
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Send(this, "quitLandScape");
}
}
In Android , need to modify MainActivity as follow :
[Activity(Label = "ForceOrientation", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, ScreenOrientation = ScreenOrientation.Portrait)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
//allowing the device to change the screen orientation based on the rotation
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
MessagingCenter.Subscribe<PageTwo>(this, "allowLandScape", sender =>
{
RequestedOrientation = ScreenOrientation.Landscape;
});
//during page close setting back to portrait
MessagingCenter.Subscribe<PageTwo>(this, "quitLandScape", sender =>
{
RequestedOrientation = ScreenOrientation.Portrait;
});
}
}
In iOS , we need to create a PageTwoRenderer as follow :
[assembly: ExportRenderer(typeof(PageTwo), typeof(PageTwoRenderer))]
namespace ForceOrientation.iOS
{
public class PageTwoRenderer : PageRenderer
{
public PageTwoRenderer()
{
MessagingCenter.Subscribe<PageTwo>(this, "allowLandScape", sender =>
{
UIDevice.CurrentDevice.SetValueForKey(NSNumber.FromNInt((int)(UIInterfaceOrientation.LandscapeLeft)), new NSString("orientation"));
});
//during page close setting back to portrait
MessagingCenter.Subscribe<PageTwo>(this, "quitLandScape", sender =>
{
UIDevice.CurrentDevice.SetValueForKey(NSNumber.FromNInt((int)(UIInterfaceOrientation.Portrait)), new NSString("orientation"));
});
}
}
}
==============================Update=============================
We can use Xamarin.Essentials: Device Information to check whether the device is Phone , Tablet or other devices .
DeviceInfo.Idiom correlates a constant string that maps to the type of device the application is running on. The values can be checked with the DeviceIdiom struct:
DeviceIdiom.Phone – Phone
DeviceIdiom.Tablet – Tablet
DeviceIdiom.Desktop – Desktop
DeviceIdiom.TV – TV
DeviceIdiom.Watch – Watch
DeviceIdiom.Unknown – Unknown
Modiied PageTwo as follow :
protected override void OnAppearing()
{
base.OnAppearing();
var idiom = DeviceInfo.Idiom;
if (idiom == DeviceIdiom.Phone)
{
MessagingCenter.Send(this, "allowLandScape");
}
}
//during page close setting back to portrait
protected override void OnDisappearing()
{
base.OnDisappearing();
var idiom = DeviceInfo.Idiom;
if (idiom == DeviceIdiom.Phone)
{
MessagingCenter.Send(this, "quitLandScape");
}
}
You can force set orientation on these few pages
// on IOS -> AppDelegate.cs
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow)
{
if (Device.Idiom == TargetIdiom.Phone ) {
{
return UIInterfaceOrientationMask.Landscape;
}
else
{
return UIInterfaceOrientationMask.Portrait;
}
}
// and on Android -> MainActivity.cs do the same if else in here
protected override void OnCreate(Bundle savedInstanceState)
{
if (Device.Idiom == TargetIdiom.Phone)
{
RequestedOrientation = ScreenOrientation.Landscape;
}
else
{
RequestedOrientation = ScreenOrientation.Portrait;
}
}
You can check about Device class more in documentation https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/device#deviceidiom

How can I hide the navigation bar BEFORE the first page has displayed?

I am building a Xamarin Android app using Xamarin.Forms my App class is really simple and contains one page like so:
public App()
{
// The root page of your application
MainPage = new NavigationPage(new ContentPage()
{
Title = "My First Page!",
Content = new Label()
{
Text = "Test Page!",
TextColor = Color.White,
FontSize = 25,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
}
});
}
and my MainActivity class like so:
[Activity(Label = "GridTest", Icon = "#drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
When I launch the application though the NavigationBar is displayed with the word "GridTest" displayed in the top left which looks rubbish, shown below:
This is the NavigationBar I would like to hide.
After this my application loads with the navigation bar fine. Shown below:
I would like to keep this Navigation Bar
So my question is. How do I hide the Android NavigationBar that is displayed BEFORE the initial page has loaded?
SIDE NOTE:
I have tried NavigationPage.SetHasNavigationBar(page,false); but this does not work. This hides the NavigationBar for the first page shown below.
which I would like to keep! This also does not hide the "GridTest" navigation bar I am looking to hide (shown in screenshot 1)
To reiterate... I am looking to hide the INITIAL navigation bar. provided by the Android MainActivity NOT the Xamarin.Forms navigation bar
The main activity which will load the first time the application starts.
[Activity(Label = "testapp", MainLauncher = true, Theme = "#style/MyTheme", ScreenOrientation = ScreenOrientation.Portrait)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// SetContentView(Resource.Layout.LaunchScreen); //Set a splash screen here if you wish
Java.Lang.Runnable runnable = new Java.Lang.Runnable(() =>
{
Intent i = new Intent(this, typeof(MainActivity));
StartActivity(i);
});
new Handler().PostDelayed(runnable, 1000);
}
}
The theme for that activity must be saved to Resources/drawable/styles.xml and is
<resources>
<style name="MyTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowNoTitle">true</item>
</style>
</resources>
This SplashActivity will load and call MainActivity
and your MainActivity will look like this:
[Activity(Label = "GridTest", Icon = "#drawable/icon", ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
You dont need to set the Theme here and it will work as before. Another Important note about the MainActivity is you MUST remove MainLauncher = true
You should be able to hide that navigation bar by setting a simple attribute in your page like so:
<ContentPage NavigationPage.HasNavigationBar="false"
..... >
</ContentPage>

Categories