1111using Java . Lang ;
1212using Serilog ;
1313using Exception = System . Exception ;
14+ using AndroidLog = Android . Util . Log ;
1415
1516namespace Bible . Alarm . Platforms . Android ;
1617
1718[ Activity ( Label = "Bible Alarm" , Theme = "@style/MainTheme" , LaunchMode = LaunchMode . SingleTop , MainLauncher = true , ConfigurationChanges = ConfigChanges . ScreenSize | ConfigChanges . Orientation | ConfigChanges . UiMode | ConfigChanges . ScreenLayout | ConfigChanges . SmallestScreenSize | ConfigChanges . Density ) ]
1819public class MainActivity : MauiAppCompatActivity
1920{
20- private static readonly ILogger logger = Log . ForContext < MainActivity > ( ) ;
21+ // Lazy logger initialization to ensure Serilog is configured first
22+ private static ILogger ? logger ;
23+ private static ILogger Logger => logger ??= Serilog . Log . ForContext < MainActivity > ( ) ;
24+
2125 private IAndroidAlarmHandler ? alarmHandler ;
2226 private DateTime ? lastResumeTime ;
2327
2428 protected override void OnCreate ( Bundle ? savedInstanceState )
2529 {
26- // IMPORTANT: Create MAUI app BEFORE calling base.OnCreate()
27- // This ensures the service provider is available when MAUI's lifecycle events try to access it
28- // If the app was previously disposed (swiped out), CreateAndStore() will create a new app instance
29- MauiAppHolder . CreateAndStore ( ) ;
30+ // Set up global exception handlers FIRST, before any other operations
31+ // This ensures we catch exceptions even if they occur during initialization
32+ AppDomain . CurrentDomain . UnhandledException += UnhandledExceptionHandler ;
33+ TaskScheduler . UnobservedTaskException += UnobservedTaskExceptionHandler ;
3034
31- // Fix for MAUI NavigationRootManager fragment state restoration issue
32- // If savedInstanceState contains stale fragment state that references non-existent views (like id/legacy),
33- // pass null to prevent fragment restoration. This can happen after app updates or when MAUI framework
34- // tries to restore fragments with view IDs that no longer exist.
35- Bundle ? safeSavedInstanceState = savedInstanceState ;
36- if ( savedInstanceState != null )
35+ // Log early using Android log in case Serilog isn't initialized yet
36+ AndroidLog . Info ( "MainActivity" , "OnCreate started" ) ;
37+
38+ try
3739 {
38- try
40+ // IMPORTANT: Create MAUI app BEFORE calling base.OnCreate()
41+ // This ensures the service provider is available when MAUI's lifecycle events try to access it
42+ // If the app was previously disposed (swiped out), CreateAndStore() will create a new app instance
43+ AndroidLog . Info ( "MainActivity" , "Calling MauiAppHolder.CreateAndStore()" ) ;
44+ MauiAppHolder . CreateAndStore ( ) ;
45+ AndroidLog . Info ( "MainActivity" , "MauiAppHolder.CreateAndStore() completed" ) ;
46+
47+ // Fix for MAUI NavigationRootManager fragment state restoration issue
48+ // If savedInstanceState contains stale fragment state that references non-existent views (like id/legacy),
49+ // pass null to prevent fragment restoration. This can happen after app updates or when MAUI framework
50+ // tries to restore fragments with view IDs that no longer exist.
51+ Bundle ? safeSavedInstanceState = savedInstanceState ;
52+ if ( savedInstanceState != null )
3953 {
40- // Check if savedInstanceState contains fragment state that might be stale
41- var hasFragmentState = false ;
42- foreach ( var key in savedInstanceState ? . KeySet ( ) ?? [ ] )
54+ try
4355 {
44- if ( key != null && ( key . Contains ( "fragment" ) || key . Contains ( "Fragment" ) ||
45- key . Contains ( "androidx.lifecycle" ) || key . Contains ( "android:support" ) ) )
56+ // Check if savedInstanceState contains fragment state that might be stale
57+ var hasFragmentState = false ;
58+ foreach ( var key in savedInstanceState ? . KeySet ( ) ?? [ ] )
4659 {
47- hasFragmentState = true ;
48- break ;
60+ if ( key != null && ( key . Contains ( "fragment" ) || key . Contains ( "Fragment" ) ||
61+ key . Contains ( "androidx.lifecycle" ) || key . Contains ( "android:support" ) ) )
62+ {
63+ hasFragmentState = true ;
64+ break ;
65+ }
4966 }
50- }
5167
52- if ( hasFragmentState )
68+ if ( hasFragmentState )
69+ {
70+ Logger . Information ( "Detected fragment state in savedInstanceState - ignoring to prevent NavigationRootManager crash" ) ;
71+ // Pass null to prevent fragment restoration - MAUI will recreate navigation from scratch
72+ safeSavedInstanceState = null ;
73+ }
74+ }
75+ catch ( Exception ex )
5376 {
54- logger . Information ( "Detected fragment state in savedInstanceState - ignoring to prevent NavigationRootManager crash" ) ;
55- // Pass null to prevent fragment restoration - MAUI will recreate navigation from scratch
77+ Logger . Warning ( ex , "Error checking fragment state - ignoring savedInstanceState to be safe" ) ;
5678 safeSavedInstanceState = null ;
5779 }
5880 }
59- catch ( Exception ex )
60- {
61- logger . Warning ( ex , "Error checking fragment state - ignoring savedInstanceState to be safe" ) ;
62- safeSavedInstanceState = null ;
63- }
64- }
6581
66- base . OnCreate ( safeSavedInstanceState ) ;
82+ AndroidLog . Info ( "MainActivity" , "Calling base.OnCreate()" ) ;
83+ base . OnCreate ( safeSavedInstanceState ) ;
84+ AndroidLog . Info ( "MainActivity" , "base.OnCreate() completed" ) ;
6785
68- // Set up global exception handlers
69- AppDomain . CurrentDomain . UnhandledException += UnhandledExceptionHandler ;
70- TaskScheduler . UnobservedTaskException += UnobservedTaskExceptionHandler ;
86+ // NOTE: Do NOT call InitializePlatformBootstrap here for foreground launches
87+ // WindowSetupService.CreateWindow() will handle bootstrap and send InitializedMessage
88+ // Bootstrap is only needed here for background services/jobs, not for foreground UI launches
89+
90+ AndroidLog . Info ( "MainActivity" , "Setting up intents and background tasks" ) ;
91+ // Handle incoming intents (e.g., from notifications)
92+ HandleIncomingIntent ( ) ;
7193
72- // NOTE: Do NOT call InitializePlatformBootstrap here for foreground launches
73- // WindowSetupService.CreateWindow() will handle bootstrap and send InitializedMessage
74- // Bootstrap is only needed here for background services/jobs, not for foreground UI launches
94+ // Set up background tasks
95+ SetupBackgroundTasks ( ) ;
96+
97+ AndroidLog . Info ( "MainActivity" , "OnCreate completed successfully" ) ;
98+ }
99+ catch ( Exception ex )
100+ {
101+ // Log the exception using Android log first (most reliable)
102+ AndroidLog . Error ( "MainActivity" , $ "FATAL ERROR in OnCreate: { ex . GetType ( ) . Name } : { ex . Message } ") ;
103+ AndroidLog . Error ( "MainActivity" , $ "Stack trace: { ex . StackTrace } ") ;
104+ if ( ex . InnerException != null )
105+ {
106+ AndroidLog . Error ( "MainActivity" , $ "Inner exception: { ex . InnerException . GetType ( ) . Name } : { ex . InnerException . Message } ") ;
107+ }
75108
76- // Handle incoming intents (e.g., from notifications)
77- HandleIncomingIntent ( ) ;
109+ // Also try to log with Serilog if available
110+ try
111+ {
112+ Logger . Fatal ( ex , "Fatal error in MainActivity.OnCreate - app will crash" ) ;
113+ }
114+ catch
115+ {
116+ // Serilog not available, already logged with AndroidLog
117+ }
78118
79- // Set up background tasks
80- SetupBackgroundTasks ( ) ;
119+ // Re-throw to let Android handle the crash (we can't recover from OnCreate failures)
120+ throw ;
121+ }
81122 }
82123
83- private void UnobservedTaskExceptionHandler ( object ? sender , UnobservedTaskExceptionEventArgs e ) => logger . Error ( e . Exception , "Unobserved task exception." ) ;
124+ private void UnobservedTaskExceptionHandler ( object ? sender , UnobservedTaskExceptionEventArgs e ) => Logger . Error ( e . Exception , "Unobserved task exception." ) ;
84125
85126 private void UnhandledExceptionHandler ( object sender , UnhandledExceptionEventArgs e )
86127 {
87- logger . Error ( e . ExceptionObject as Exception , "Unhandled exception. IsTerminating: {IsTerminating}" ,
128+ Logger . Error ( e . ExceptionObject as Exception , "Unhandled exception. IsTerminating: {IsTerminating}" ,
88129 e . IsTerminating ) ;
89130 }
90131
@@ -116,7 +157,7 @@ private void HandleIncomingIntent()
116157 }
117158 catch ( Exception e )
118159 {
119- logger . Error ( e , "Error handling incoming alarm intent" ) ;
160+ Logger . Error ( e , "Error handling incoming alarm intent" ) ;
120161 }
121162 } ) ;
122163 }
@@ -137,7 +178,7 @@ private void SetupBackgroundTasks()
137178 }
138179 catch ( Exception ex )
139180 {
140- logger . Error ( ex , "Error setting up background tasks" ) ;
181+ Logger . Error ( ex , "Error setting up background tasks" ) ;
141182 }
142183 } ) ;
143184 }
@@ -154,7 +195,7 @@ protected override void OnStart()
154195 catch ( IllegalArgumentException ex ) when ( ex . Message ? . Contains ( "No view found for id" ) == true && ex . Message ? . Contains ( "legacy" ) == true )
155196 {
156197 // Fragment restoration failed due to stale state - clear fragments and retry
157- logger . Warning ( ex , "Fragment restoration failed due to stale state - clearing fragments and retrying" ) ;
198+ Logger . Warning ( ex , "Fragment restoration failed due to stale state - clearing fragments and retrying" ) ;
158199 try
159200 {
160201 var fragmentManager = SupportFragmentManager ;
@@ -182,7 +223,7 @@ protected override void OnStart()
182223 }
183224 catch ( Exception retryEx )
184225 {
185- logger . Error ( retryEx , "Failed to recover from fragment restoration error - app may be in inconsistent state" ) ;
226+ Logger . Error ( retryEx , "Failed to recover from fragment restoration error - app may be in inconsistent state" ) ;
186227 // Re-throw to let Android handle it
187228 throw ;
188229 }
@@ -236,13 +277,13 @@ protected override void OnSaveInstanceState(Bundle outState)
236277 outState . Remove ( key ) ;
237278 }
238279
239- logger . Information ( "Prevented saving {Count} fragment state keys to avoid NavigationRootManager crash" , keysToRemove . Count ) ;
280+ Logger . Information ( "Prevented saving {Count} fragment state keys to avoid NavigationRootManager crash" , keysToRemove . Count ) ;
240281 }
241282 }
242283 }
243284 catch ( Exception ex )
244285 {
245- logger . Warning ( ex , "Error in OnSaveInstanceState - proceeding anyway" ) ;
286+ Logger . Warning ( ex , "Error in OnSaveInstanceState - proceeding anyway" ) ;
246287 // Still call base to ensure other state is saved
247288 try
248289 {
@@ -277,7 +318,7 @@ protected override void OnDestroy()
277318 }
278319 catch ( Exception ex )
279320 {
280- logger . Error ( ex , "Error in MainActivity.OnDestroy while attempting to dismiss player" ) ;
321+ Logger . Error ( ex , "Error in MainActivity.OnDestroy while attempting to dismiss player" ) ;
281322 }
282323
283324
0 commit comments