348 lines
11 KiB
Dart
348 lines
11 KiB
Dart
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<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
await Firebase.initializeApp();
|
|
await NotificationServce.display(message);
|
|
}
|
|
|
|
String currentVersion = '';
|
|
String storeVersion = '';
|
|
bool updateRequired = false;
|
|
|
|
Future<void> 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<void> 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<bool> 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<void> 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<ConnectivityResult> _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<void> _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<RiderLogController>()) {
|
|
final ctl = Get.find<RiderLogController>();
|
|
// 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;
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|