Xamarin Android possible ScreenOrientation.Landscape bug - c#

my app had some strange behaviour so I recreated the app until the point where it bugged and found out that ScreenOrientation.Landscape is the culprit.
If you make a new blank app in visual studio 15 and replace the MainActivity with:
[Activity(Label = "TestLandscapeBug", MainLauncher = true, Icon = "#drawable/icon",
ScreenOrientation = ScreenOrientation.Landscape)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
System.Console.WriteLine("OnCreate");
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
}
protected override void OnDestroy()
{
System.Console.WriteLine("OnDestroy");
base.OnDestroy();
}
protected override void OnPause()
{
System.Console.WriteLine("OnPause");
base.OnPause();
}
protected override void OnRestart()
{
System.Console.WriteLine("OnRestart");
base.OnRestart();
}
protected override void OnResume()
{
System.Console.WriteLine("OnResume");
base.OnResume();
}
protected override void OnStart()
{
System.Console.WriteLine("OnStart");
base.OnStart();
}
protected override void OnStop()
{
System.Console.WriteLine("OnStop");
base.OnStop();
}
}
run the app and press the sleep button:
OnPause, OnStop, OnDestroy, OnCreate, OnStart, OnResume and OnPause are called.
if you remove ScreenOrientation = ScreenOrientation.Landscape OnPause and OnStop are called.
Is this a bug? Or am I doing something wrong?
how can I fix this or use something else which locks the screen in landscape.

It's normal for an activity to switch to portrait mode when the screen is locked. Whenever the orientation changes, OnDestroy is called followed by OnCreate. So, there's nothing to worry about as what you're witnessing is default behavior of Android.
Portrait is kind of the default orientation for the lockscreen so it makes sense that your activity also switches to that when locking.

Related

Xamarin.Forms Firebasepushnotification plugin OnNotificationOpened event causes app to restart for android

I'm trying to handle a notifications opening on android that I've sent through Google's FCM on the SharedProject level by using "Plugin.FirebasePushNotification" plugin and subscribing to "OnNotificationOpened" event.
When I try to open the notification while the app is in the background I'm able to hit a breakpoint inside "OnNotificationOpened" event but after the work inside the event ends the App const. is hit and the application restarts.
I want the application to continue from its last state and hit "OnResume" method that is located inside the App class.
I've done some research about it online and almost all of them were about how splash activity might cause this issue so I've disabled my splash activity by turning the whole "SplashActivity.cs" file into a comment except the namespace and usages and implemented the wanted features of splashactivity(style) inside the "MainActivity" class after that I made sure my "MainActivity" class was the only activity that had it's "MainLauncher" attribute set to true but the problem continues.
Here are the relevant classes:
FirebaseReg.cs
public class FirebaseReg
{
public static void FirebaseInit()
{
CrossFirebasePushNotification.Current.OnTokenRefresh += (s, p) =>
{
System.Diagnostics.Debug.WriteLine($"TOKEN : {p.Token}");
CrossFirebasePushNotification.Current.Subscribe("defaultTopic");
};
CrossFirebasePushNotification.Current.OnNotificationOpened += (s, p) =>
{
try
{
//Breakpoint hits here and I'm able to finish all my work without any exceptions
}
catch (Exception ex)
{
}
};
CrossFirebasePushNotification.Current.OnNotificationAction += (s, p) =>
{
};
CrossFirebasePushNotification.Current.OnNotificationReceived += Current_OnNotificationReceived;
}
private static void Current_OnNotificationReceived(object source, FirebasePushNotificationDataEventArgs e)
{
//DO WORK
}
}
App.xaml.cs
public partial class App : Application
{
public static Uri ServerUri = new Uri(" ");
public static bool IsInForeground { get; set; } = false;
public App()
{
try
{
InitializeComponent(); //After the work in OnNotificationOpened ends breakpoint goes here
Application.Current.UserAppTheme = OSAppTheme.Unspecified;
Application.Current.MainPage = new LoginPage();
}
catch (Exception ex)
{
}
}
protected override void OnStart()
{
IsInForeground = true;
}
protected override void OnSleep()
{
IsInForeground = false;
}
protected override void OnResume()
{
IsInForeground = true;
}
}
MainActivity.cs
[Activity(Label = "TestProject1", Theme = "#style/MyTheme.Splash", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.Window.RequestFeature(WindowFeatures.ActionBar);
// Name of the MainActivity theme you had there before.
// Or you can use global::Android.Resource.Style.ThemeHoloLight
base.SetTheme(Resource.Style.MainTheme);
base.OnCreate(savedInstanceState);
Rg.Plugins.Popup.Popup.Init(this);
UserDialogs.Init(this);
Xamarin.FormsGoogleMaps.Init(this, savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
FirebasePushNotificationManager.ProcessIntent(this ,Intent);
LoadApplication(new App());
}
protected override void OnNewIntent(Intent intent)
{
FirebasePushNotificationManager.ProcessIntent(this, intent);
base.OnNewIntent(intent);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
MainApplication.cs
[Application]
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
{
public MainApplication(IntPtr handle, JniHandleOwnership transer) : base(handle, transer)
{
}
public override void OnCreate()
{
base.OnCreate();
RegisterActivityLifecycleCallbacks(this);
if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
FirebasePushNotificationManager.DefaultNotificationChannelId = "DefaultChannel";
FirebasePushNotificationManager.DefaultNotificationChannelName = "defaultTopic";
}
#if DEBUG
FirebasePushNotificationManager.Initialize(this, true);
FirebaseRegister.FirebaseInit();
#else
FirebasePushNotificationManager.Initialize(this, false);
FirebaseRegister.FirebaseInit();
#endif
}
public override void OnTerminate()
{
base.OnTerminate();
UnregisterActivityLifecycleCallbacks(this);
}
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityDestroyed(Activity activity)
{
}
public void OnActivityPaused(Activity activity)
{
}
public void OnActivityResumed(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
{
}
public void OnActivityStarted(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityStopped(Activity activity)
{
}
}
From the Android docs, it states this:
If it has declared its launch mode to be "multiple" (the default) and
you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it
will be finished and re-created; for all other launch modes or if
FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to
the current instance's onNewIntent().
That's why your app is restarting.
The launchMode attribute of the activity affects how the activity is launched.
singleTop, singleTask, or singleInstance should be used to prevent the notification intent from creating a new activity instance.
The flag FLAG_ACTIVITY_NEW_TASK doesn't influence a new activity being created, but makes the launched activity the root of a new task.
For more, check:
https://developer.android.com/guide/topics/manifest/activity-element.html#lmode
https://developer.android.com/guide/components/activities/tasks-and-back-stack

BroadcastReceiver must provide a public default constructor - Xamarin.Android

Ok so I am running a BroadcastReceiver:
public class SyncServiceBroadcastReceiver : BroadcastReceiver
{
public static string syncStarted = "SyncRuns";
private readonly PendingOrdersActivity pendingOrdersActivity;
public SyncServiceBroadcastReceiver(PendingOrdersActivity pendingOrdersActivity)
{
this.pendingOrdersActivity = pendingOrdersActivity;
}
public override void OnReceive(Context context, Intent intent)
{
this.pendingOrdersActivity.RefreshPendingOrdersOnSync();
Toast.MakeText(context, "Datele se actualizează...", ToastLength.Short).Show();
}
}
Each time it hits, I want it to run a method from the activity (method RefreshPendingOrdersOnSync). That's why I've created a constructor which takes in an activity.
In the activity I am registering the BroadcastReceiver, in the OnResume method, by passing in "this":
var filter = new IntentFilter(SyncServiceBroadcastReceiver.syncStarted);
filter.AddCategory(Intent.CategoryDefault);
this.syncServiceBroadcastReceiver = new SyncServiceBroadcastReceiver(this);
this.RegisterReceiver(this.syncServiceBroadcastReceiver, filter);
Still, on the Receiver part, it gives the error from the title....
If I add an empty constructor, I suspect it goes through there and this line in the OnReceive breakes, because this.pendingOrdersActivity is null
this.pendingOrdersActivity.RefreshPendingOrdersOnSync();
I am quite puzzled why
BroadcastReceiver must provide a public default constructor
The default constructor method is required. Because when registering BroadcastReceiver using AndroidManifest.xml, Android uses reflection to instantiate the receiver and it needs a default constructor.
Refer to:https://stackoverflow.com/a/14266261/11083277
If I add an empty constructor, I suspect it goes through there and this line in the OnReceive breakes
I tested a basic demo to test the code, add the default constructor method and the code works fine. The parameterized constructor will be executed. Did you register the receiver in AndroidManifest.xml? If so, try to do the work in code.
[BroadcastReceiver]
public class CustomReceiver : BroadcastReceiver
{
private readonly MainActivity activity;
public CustomReceiver()
{
}
public CustomReceiver(MainActivity activity)
{
this.activity = activity;
}
public override void OnReceive(Context context, Intent intent)
{
activity.method();
}
}
Activity class
public class MainActivity : AppCompatActivity
{
CustomReceiver receiver;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
receiver = new CustomReceiver(this);
}
public void method()
{
...
}
protected override void OnResume()
{
base.OnResume();
RegisterReceiver(receiver, new IntentFilter("package_name"));
}
protected override void OnDestroy()
{
base.OnDestroy();
UnregisterReceiver(receiver);
}
}

Xamarin.Forms Background Service prevent to start when app is running

I managed to implement my Background Service which works really good. The Service connects to my WebSocket and keeps the connection alive after the App is closed.
I'm starting the Background Service when the App closes so that I can reconnect to the Backend, but I want to be able to close the Service so the App can now take care of the Connection.
I've tried Flags, Events, and other conditions to Stop the Service when the App starts but the Service Starts no matter what.
Is it possible to stop the Service when the App runs, so that the App can handle the Connection?
MainActivity.cs
[Activity(Label = "App",
Icon = "#drawable/ic_launcher",
Theme = "#style/splashscreen",
MainLauncher = true,
ScreenOrientation = ScreenOrientation.Portrait)]
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
SetTheme(Resource.Style.MainTheme);
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
Forms.Init(this, bundle);
LoadApplication(new App());
StartBackgroundService();
IsAppRunning = true;
}
private void StartBackgroundService() {
StopService = false;
RealtimeService.StopServiceEventHandler += StopServiceEventHandler;
StartService(new Intent(Forms.Context, typeof(BackgroundRealtimeService)));
}
private void StopServiceEventHandler(object sender, object o) {
if (StopService) {
StopService(new Intent(Forms.Context, typeof(BackgroundRealtimeService)));
}
}
protected override void OnDestroy()
{
IsAppRunning = false;
base.OnDestroy();
}
}
}
BackgroundRealtimeService.cs
[Service]
public class BackgroundRealtimeService : Service
{
public async void Initialize() {
// Service Code
}
public override void OnCreate() {
base.OnCreate();
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) {
new Task(Initialize).Start();
return StartCommandResult.Sticky;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
}
}

You MUST call Xamarin.Forms.Init(); prior to using it

In my app.xaml.cs I create a new page.
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new WrapLayoutPage());
}
This page calls a static class, which uses the DependencyService to perform some tasks.
The line which throws the error:
var tmpTable = SqLiteHelper.GetItem<TableX>("someId");
SqLiteHelper:
public static class SqLiteHelper
{
private static readonly SQLiteConnection DatabaseConnection = DependencyService.Get<ISqLite>().GetConnection();
private static readonly object Locker = new object();
public static DbObjectV3 GetItem<T>(Guid inId) where T : DbObjectV3, new()
{
lock (Locker)
{
var tmpItem = DatabaseConnection.Table<T>().FirstOrDefault(inItem => inItem.Id == inId);
tmpItem.IsNewObject = false;
return tmpItem;
}
}
}
This throws me a TypeInitializationException with the InnerException:
You MUST call Xamarin.Forms.Init(); prior to using it
It's somehow related to the static helper class, because prior to that call, I can use the DependencyService without any problems!
As mainlauncher I'm using a splash screen. In this class I do some startup work, which relies on the DependencyService.
SplashScreen:
[Activity(Theme = "#style/MyTheme.Splash", NoHistory = true, MainLauncher = true)]
public class SplashScreen : Activity
{
static readonly string TAG = "X:" + typeof(SplashScreen).Name;
public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState)
{
base.OnCreate(savedInstanceState, persistentState);
Log.Debug(TAG, "SplashActivity.OnCreate");
}
}
My MainActivity:
[Activity(Label = "FrameworkForms", Icon = "#drawable/icon", ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, Theme = "#style/MainActivityTheme", MainLauncher = false)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Xamarin.Forms.Forms.Init(this, bundle);
App.ScreenWidth = (double)(Resources.DisplayMetrics.WidthPixels / Resources.DisplayMetrics.Density);
LoadApplication(new App());
}
}
Now after changing the Activity in SplashScreen to FormsAppCompatActivity, I get another error.
Call Forms.Init() before Hide Keyboard
What's the thing here?
This is pretty unfortunate. I used the wrong OnCreate() method in my SplashScreen.
I changed SplashScreen to:
[Activity(Theme = "#style/MyTheme.Splash", NoHistory = true, MainLauncher = true)]
public class SplashScreen : Activity
{
static readonly string TAG = "X:" + typeof(SplashScreen).Name;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Forms.Forms.Init(this, savedInstanceState);
Log.Debug(TAG, "SplashActivity.OnCreate");
}
protected override void OnResume()
{
base.OnResume();
Task tmpStartupWork = new Task(() =>
{
Log.Debug(TAG, "Performing some startup work that takes a bit of time.");
StartUpTasks.InitializeDatabaseCreation();
Log.Debug(TAG, "Working in the background - important stuff.");
});
tmpStartupWork.ContinueWith(inT =>
{
Log.Debug(TAG, "Work is finished - start MainActivity.");
StartActivity(new Intent(Application.Context, typeof(MainActivity)));
}, TaskScheduler.FromCurrentSynchronizationContext());
tmpStartupWork.Start();
}
}
Unfortunately, the documentation on Xamarin about creating a splash screen uses the OnCreate() method with 2 parameters!
I also came across this mistake. The cause of the error was that I wanted to change my namespace. From
public partial class AppDelegate :
global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
I changed it to:
using Xamarin.Forms.Platform.iOS;
public partial class AppDelegate : FormsApplicationDelegate
Then I saw the following:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
I thought, it would be useful to adjust that as well... without thinking more exactly about it. And I wrote:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
this.Init();
A while later, I got the mistake and had to laboriously search for the cause. Finally I found and fixed it. Maybe it can help someone :-) Cheers...

How could I know if all my modules are loaded in prism 4?

I have a WPF Desktop application using PRISM, there are 12 modules which do not depend on each other , every time i start the shell, modules are been loaded , the point is that I would like to know which module loads at the last so I could start an action. How could I determine this ?
Override Bootstrapper.InitializeModules, call base, and then ACTION!
Expanding on erikH's answer (thank you, btw), assuming that you are deriving from the default UnityBootstrapper, here is the order in which the typically overridden methods are called:
//0
public override void Run(bool runWithDefaultConfiguration)
{
base.Run(runWithDefaultConfiguration);
//this is our last opportunity to hook into the PRISM bootstrapping sequence; at this point every very other base-overridden
//method has been executed
}
//1
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
//add modules...
}
//2
protected override void ConfigureContainer()
{
base.ConfigureContainer();
//register everything with the container...
}
//3
protected override DependencyObject CreateShell()
{
return Container.Resolve<ShellView>(); //resolve your root component
}
//4
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
}
//5
protected override void InitializeModules()
{
base.InitializeModules();
}
Note that this applies to PRISM 4 and 5

Categories

Resources