import 'dart:io'; import 'package:nearle/helpers/http_overrides.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:get/get.dart'; import 'package:nearle/views/introscreens/splashscreen.dart'; import 'package:nearle/controllers/profile_controller.dart'; import 'package:nearle/controllers/riderlog.dart'; import 'package:nearle/controllers/delivery.dart'; import 'package:nearle/controllers/logcontroller.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:nearle/providers/notifications/notificationservce.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:new_version_plus/new_version_plus.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:nearle/views/updatescreen/UpdateScreen.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; // ⭐ ADDED import 'package:nearle/background/backgroundservice.dart'; import 'package:nearle/helpers/shift_end_alarm.dart'; import 'views/offline/offline_page.dart'; // Background message handler @pragma('vm:entry-point') Future firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); await NotificationServce.display(message); } String currentVersion = ''; String storeVersion = ''; bool updateRequired = false; Future getAppVersion() async { SharedPreferences prefs = await SharedPreferences.getInstance(); PackageInfo packageInfo = await PackageInfo.fromPlatform(); String version = packageInfo.version; currentVersion = version; prefs.setString('CurrentVersion', currentVersion); print('Current version from main: $currentVersion'); } Future checkForUpdate(BuildContext context) async { final newVersion = NewVersionPlus( iOSId: '284882215', androidId: "com.nearle.partner", ); final status = await newVersion.getVersionStatus(); print('The status = $status'); if (status != null) { print("Current Version: ${status.localVersion}"); print("Store Version: ${status.storeVersion}"); print("Can Update: ${status.canUpdate}"); if (status.canUpdate) { currentVersion = status.localVersion; storeVersion = status.storeVersion; updateRequired = true; Get.offAll( () => UpdateScreen( mCurrentVersion: status.localVersion, mUpdateVersion: status.storeVersion, mIsForceUpdate: true, ), transition: Transition.fadeIn, ); } } } Future recheckVersion() async { final newVersion = NewVersionPlus( iOSId: '284882215', androidId: "com.nearle.partner", ); try { final status = await newVersion.getVersionStatus(); if (status != null) { print( "Recheck - Current: ${status.localVersion}, Store: ${status.storeVersion}", ); return status.canUpdate; } } catch (e) { print("Error rechecking version: $e"); } return false; } Future main() async { WidgetsFlutterBinding.ensureInitialized(); HttpOverrides.global = MyHttpOverrides(); await Firebase.initializeApp(); FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); Get.put(ProfileController(), permanent: true); Get.put(RiderLogController(), permanent: true); Get.put(DeliveryController(), permanent: true); final logController = Get.put(LogController(), permanent: true); await logController.startLogging(); SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarColor: Colors.white, statusBarIconBrightness: Brightness.dark, statusBarBrightness: Brightness.light, systemNavigationBarColor: Colors.white, systemNavigationBarIconBrightness: Brightness.dark, ), ); runApp(const _RootApp()); } class _RootApp extends StatefulWidget { const _RootApp(); @override State<_RootApp> createState() => _RootAppState(); } class _RootAppState extends State<_RootApp> with WidgetsBindingObserver { List _connectionStatus = [ConnectivityResult.none]; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); Connectivity().onConnectivityChanged.listen((result) { if (mounted) { setState(() { _connectionStatus = result; }); } }); Connectivity().checkConnectivity().then((result) { if (mounted) { setState(() { _connectionStatus = result; }); } }); // ✅ Check if shift ended while app was killed OR if shift end time has already passed _checkShiftEndOnStartup(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } /// Check if shift ended while app was killed OR if shift end time has already passed /// This will trigger break log immediately if rider is on duty and shift end time passed Future _checkShiftEndOnStartup() async { try { final prefs = await SharedPreferences.getInstance(); final int onduty = prefs.getInt('onduty') ?? 0; // Only check if rider was on duty if (onduty != 1) { debugPrint('[APP_STARTUP] Rider not on duty, skipping shift end check'); return; } // Check if shift has ended final String endTimeStr = prefs.getString('endtime') ?? ''; if (endTimeStr.isEmpty) { debugPrint('[APP_STARTUP] No endtime found, skipping shift end check'); return; } debugPrint( '[APP_STARTUP] Checking shift end - Current time: ${DateTime.now()}, End time: $endTimeStr', ); // Use the same logic as BackgroundDeliveryLog.checkShiftEnd() final now = DateTime.now(); final endParts = endTimeStr.split(':'); if (endParts.length < 2) return; final int endHour = int.tryParse(endParts[0]) ?? 0; final int endMinute = int.tryParse(endParts[1]) ?? 0; final int endSecond = endParts.length > 2 ? (int.tryParse(endParts[2]) ?? 0) : 0; final DateTime endToday = DateTime( now.year, now.month, now.day, endHour, endMinute, endSecond, ); bool isShiftOver = false; final String startTimeStr = prefs.getString('starttime') ?? ''; if (startTimeStr.isNotEmpty) { final startParts = startTimeStr.split(':'); if (startParts.length >= 2) { final int startHour = int.tryParse(startParts[0]) ?? 0; final int startMinute = int.tryParse(startParts[1]) ?? 0; final double startVal = startHour + (startMinute / 60.0); final double endVal = endHour + (endMinute / 60.0); if (startVal > endVal) { // Overnight shift final DateTime startToday = DateTime( now.year, now.month, now.day, startHour, startMinute, ); if (now.isAfter(endToday) && now.isBefore(startToday)) { isShiftOver = true; } } else { // Normal day shift if (now.isAfter(endToday)) { isShiftOver = true; } } } else { if (now.isAfter(endToday)) { isShiftOver = true; } } } else { if (now.isAfter(endToday)) { isShiftOver = true; } } if (isShiftOver) { debugPrint( '[APP_STARTUP] ⏰ Shift end time ($endTimeStr) has already passed - creating break log IMMEDIATELY...', ); // Call the background service to create break log await BackgroundDeliveryLog.checkShiftEnd(); debugPrint('[APP_STARTUP] ✅ Break log created (if needed)'); } else { debugPrint( '[APP_STARTUP] Shift end time ($endTimeStr) has not passed yet - scheduling alarm', ); // Also reschedule the alarm in case it was cancelled try { final String startTime = prefs.getString('starttime') ?? ''; await ShiftEndAlarm.scheduleAlarm( endTime: endTimeStr, startTime: startTime, ); } catch (e) { debugPrint('[APP_STARTUP] Error rescheduling alarm: $e'); } } } catch (e) { debugPrint('[APP_STARTUP] Error checking shift end: $e'); } } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.resumed) { // When app comes back to foreground, ensure rider logs resume try { if (Get.isRegistered()) { final ctl = Get.find(); // Fire an immediate rider log (best-effort) ctl.createLoginNowV2(); // Ensure the periodic background/foreground loop is running ctl.startAutoCreateLoginLoop(); } } catch (_) { // Ignore lifecycle errors; app should not crash because of logging } } } bool get _isOffline { return _connectionStatus.isEmpty || _connectionStatus.every((status) => status == ConnectivityResult.none); } @override Widget build(BuildContext context) { // ⭐ RESPONSIVE WRAPPER ADDED return ScreenUtilInit( designSize: const Size(390, 844), // YOUR BASE SIZE minTextAdapt: true, splitScreenMode: true, builder: (_, __) { return GetMaterialApp( debugShowCheckedModeBanner: false, title: 'Nearle partner', theme: ThemeData( fontFamily: 'Proxima Nova', useMaterial3: true, appBarTheme: const AppBarTheme( backgroundColor: Colors.white, elevation: 0, foregroundColor: Colors.black, systemOverlayStyle: SystemUiOverlayStyle( statusBarColor: Colors.white, statusBarIconBrightness: Brightness.dark, statusBarBrightness: Brightness.light, ), ), scaffoldBackgroundColor: Colors.white, ), home: const Splashscreen(), builder: (context, child) { final baseChild = child ?? const SizedBox.shrink(); if (_isOffline) { return Stack(children: [baseChild, const OfflinePage()]); } return baseChild; }, ); }, ); } }