Copilot edit

This commit is contained in:
mohamadmahdi jebeli 2025-08-05 11:36:05 +03:30
parent 60c91e8fbb
commit b2c471c26e
6 changed files with 304 additions and 155 deletions

View File

@ -1,6 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -13,23 +12,15 @@ import 'package:proxibuy/presentation/comment/bloc/comment_bloc.dart'; // این
import 'package:proxibuy/presentation/notification_preferences/bloc/notification_preferences_bloc.dart'; import 'package:proxibuy/presentation/notification_preferences/bloc/notification_preferences_bloc.dart';
import 'package:proxibuy/presentation/offer/bloc/offer_bloc.dart'; import 'package:proxibuy/presentation/offer/bloc/offer_bloc.dart';
import 'package:proxibuy/presentation/reservation/cubit/reservation_cubit.dart'; import 'package:proxibuy/presentation/reservation/cubit/reservation_cubit.dart';
import 'package:proxibuy/services/background_service.dart';
import 'package:proxibuy/services/mqtt_service.dart'; import 'package:proxibuy/services/mqtt_service.dart';
import 'core/config/app_colors.dart'; import 'core/config/app_colors.dart';
import 'package:proxibuy/presentation/pages/splash_screen.dart'; import 'package:proxibuy/presentation/pages/splash_screen.dart';
void main() async { void main() async {
Future<String?> getFcmToken() async {
FirebaseMessaging messaging = FirebaseMessaging.instance;
String? token = await messaging.getToken();
print("🔥 Firebase Messaging Token: $token");
return token;
}
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await initializeService(); // Background service را بعداً initialize خواهیم کرد
// await initializeService();
HttpOverrides.global = MyHttpOverrides(); HttpOverrides.global = MyHttpOverrides();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

View File

@ -9,7 +9,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_background_service/flutter_background_service.dart'; import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart' as geo;
import 'package:permission_handler/permission_handler.dart';
import 'package:proxibuy/core/config/api_config.dart'; import 'package:proxibuy/core/config/api_config.dart';
import 'package:proxibuy/core/config/app_colors.dart'; import 'package:proxibuy/core/config/app_colors.dart';
import 'package:proxibuy/core/gen/assets.gen.dart'; import 'package:proxibuy/core/gen/assets.gen.dart';
@ -40,7 +41,9 @@ class _OffersPageState extends State<OffersPage> {
List<String> _selectedCategories = []; List<String> _selectedCategories = [];
StreamSubscription? _locationServiceSubscription; StreamSubscription? _locationServiceSubscription;
StreamSubscription? _connectivitySubscription; StreamSubscription? _connectivitySubscription;
Timer? _permissionCheckTimer;
bool _isGpsEnabled = false; bool _isGpsEnabled = false;
bool _isNotificationEnabled = false;
bool _isConnectedToInternet = true; bool _isConnectedToInternet = true;
@override @override
@ -50,12 +53,14 @@ class _OffersPageState extends State<OffersPage> {
_initializePage(); _initializePage();
_initConnectivityListener(); _initConnectivityListener();
_fetchInitialReservations(); _fetchInitialReservations();
_startPermissionMonitoring();
} }
@override @override
void dispose() { void dispose() {
_locationServiceSubscription?.cancel(); _locationServiceSubscription?.cancel();
_connectivitySubscription?.cancel(); _connectivitySubscription?.cancel();
_permissionCheckTimer?.cancel();
super.dispose(); super.dispose();
} }
@ -170,26 +175,64 @@ class _OffersPageState extends State<OffersPage> {
void _initLocationListener() { void _initLocationListener() {
_checkInitialGpsStatus(); _checkInitialGpsStatus();
_locationServiceSubscription = _locationServiceSubscription =
Geolocator.getServiceStatusStream().listen((status) { geo.Geolocator.getServiceStatusStream().listen((status) async {
final isEnabled = status == ServiceStatus.enabled; final isServiceEnabled = status == geo.ServiceStatus.enabled;
if (mounted && _isGpsEnabled != isEnabled) {
// Also check permission when service status changes
bool hasPermission = false;
if (isServiceEnabled) {
try {
geo.LocationPermission permission = await geo.Geolocator.checkPermission();
hasPermission = permission == geo.LocationPermission.whileInUse ||
permission == geo.LocationPermission.always;
} catch (e) {
debugPrint("Error checking permission in listener: $e");
}
}
final isGpsEnabledNew = isServiceEnabled && hasPermission;
if (mounted && _isGpsEnabled != isGpsEnabledNew) {
setState(() { setState(() {
_isGpsEnabled = isEnabled; _isGpsEnabled = isGpsEnabledNew;
}); });
if (!isEnabled) { if (!isGpsEnabledNew) {
context.read<OffersBloc>().add(ClearOffers()); context.read<OffersBloc>().add(ClearOffers());
// Show GPS dialog if GPS becomes disabled
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
showGPSDialog(context);
}
});
} }
} }
}); });
} }
Future<void> _checkInitialGpsStatus() async { Future<void> _checkInitialGpsStatus() async {
final status = await Geolocator.isLocationServiceEnabled(); try {
// Check if location service is enabled
final serviceEnabled = await geo.Geolocator.isLocationServiceEnabled();
// Check location permission
geo.LocationPermission permission = await geo.Geolocator.checkPermission();
bool hasPermission = permission == geo.LocationPermission.whileInUse ||
permission == geo.LocationPermission.always;
if (mounted) { if (mounted) {
setState(() { setState(() {
_isGpsEnabled = status; _isGpsEnabled = serviceEnabled && hasPermission;
}); });
} }
} catch (e) {
debugPrint("Error checking GPS status: $e");
if (mounted) {
setState(() {
_isGpsEnabled = false;
});
}
}
} }
Future<void> _loadPreferences() async { Future<void> _loadPreferences() async {
@ -204,6 +247,55 @@ class _OffersPageState extends State<OffersPage> {
} }
} }
void _startPermissionMonitoring() {
// Initial check
_checkAllPermissions();
// Periodic check every 5 seconds
_permissionCheckTimer = Timer.periodic(const Duration(seconds: 5), (timer) {
_checkAllPermissions();
});
}
Future<void> _checkAllPermissions() async {
if (!mounted) return;
// Check GPS permission and service
await _checkInitialGpsStatus();
// Check notification permission
await _checkInitialNotificationStatus();
}
Future<void> _checkInitialNotificationStatus() async {
try {
final status = await Permission.notification.status;
final isEnabled = status.isGranted;
if (mounted && _isNotificationEnabled != isEnabled) {
setState(() {
_isNotificationEnabled = isEnabled;
});
// Show notification dialog if permission is revoked
if (!isEnabled) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
showNotificationPermissionDialog(context);
}
});
}
}
} catch (e) {
debugPrint("Error checking notification status: $e");
if (mounted) {
setState(() {
_isNotificationEnabled = false;
});
}
}
}
Future<void> _handleRefresh() { Future<void> _handleRefresh() {
final completer = Completer<void>(); final completer = Completer<void>();
final service = FlutterBackgroundService(); final service = FlutterBackgroundService();
@ -530,7 +622,7 @@ class OffersView extends StatelessWidget {
const SizedBox(height: 60), const SizedBox(height: 60),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
await Geolocator.openLocationSettings(); await geo.Geolocator.openLocationSettings();
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: AppColors.confirm, backgroundColor: AppColors.confirm,

View File

@ -3,6 +3,7 @@ import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:geolocator/geolocator.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:proxibuy/core/config/app_colors.dart'; import 'package:proxibuy/core/config/app_colors.dart';
import 'package:proxibuy/presentation/auth/bloc/auth_bloc.dart'; import 'package:proxibuy/presentation/auth/bloc/auth_bloc.dart';
@ -10,6 +11,7 @@ import 'package:proxibuy/presentation/pages/onboarding_page.dart';
import 'package:proxibuy/presentation/pages/offers_page.dart'; import 'package:proxibuy/presentation/pages/offers_page.dart';
import 'package:proxibuy/core/gen/assets.gen.dart'; import 'package:proxibuy/core/gen/assets.gen.dart';
import 'package:proxibuy/services/mqtt_service.dart'; import 'package:proxibuy/services/mqtt_service.dart';
import 'package:proxibuy/services/background_service.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
class SplashScreen extends StatefulWidget { class SplashScreen extends StatefulWidget {
@ -212,11 +214,35 @@ class _SplashScreenState extends State<SplashScreen> {
} }
Future<void> _requestPermissions() async { Future<void> _requestPermissions() async {
try {
// Request notification permission
await Permission.notification.request(); await Permission.notification.request();
var status = await Permission.location.request(); // Check and request location permission
if (status.isGranted) { LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
// Only request "always" permission if basic location is granted
if (permission == LocationPermission.whileInUse ||
permission == LocationPermission.always) {
// Request background location permission using permission_handler
await Permission.locationAlways.request(); await Permission.locationAlways.request();
} }
debugPrint("Location permission status: $permission");
// حالا که permissions گرفته شد، background service را راهاندازی کنیم
try {
await initializeService();
debugPrint("Background Service initialized successfully");
} catch (e) {
debugPrint("Error initializing Background Service: $e");
}
} catch (e) {
debugPrint("Error requesting permissions: $e");
}
} }
} }

View File

@ -5,8 +5,13 @@ import 'package:proxibuy/core/config/app_colors.dart';
import 'package:proxibuy/core/gen/assets.gen.dart'; import 'package:proxibuy/core/gen/assets.gen.dart';
Future<void> showGPSDialog(BuildContext context) async { Future<void> showGPSDialog(BuildContext context) async {
try {
bool isLocationEnabled = await Geolocator.isLocationServiceEnabled(); bool isLocationEnabled = await Geolocator.isLocationServiceEnabled();
if (!isLocationEnabled) { LocationPermission permission = await Geolocator.checkPermission();
bool hasPermission = permission == LocationPermission.whileInUse ||
permission == LocationPermission.always;
if (!isLocationEnabled || !hasPermission) {
await showDialog( await showDialog(
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
context: context, context: context,
@ -39,9 +44,11 @@ Future<void> showGPSDialog(BuildContext context) async {
), ),
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
const Text( Text(
"برای اینکه بتونیم تخفیف‌های اطرافت رو سریع بهت اطلاع بدیم، اجازه بده به موقعیت مکانیت دسترسی داشته باشیم.", !isLocationEnabled
style: TextStyle( ? "برای اینکه بتونیم تخفیف‌های اطرافت رو سریع بهت اطلاع بدیم، لطفاً GPS رو فعال کن."
: "برای اینکه بتونیم تخفیف‌های اطرافت رو سریع بهت اطلاع بدیم، اجازه بده به موقعیت مکانیت دسترسی داشته باشیم.",
style: const TextStyle(
color: AppColors.hint, color: AppColors.hint,
fontSize: 16, fontSize: 16,
), ),
@ -67,13 +74,17 @@ Future<void> showGPSDialog(BuildContext context) async {
horizontal: 45, vertical: 7), horizontal: 45, vertical: 7),
), ),
onPressed: () async { onPressed: () async {
if (!isLocationEnabled) {
await Geolocator.openLocationSettings(); await Geolocator.openLocationSettings();
} else if (!hasPermission) {
await Geolocator.requestPermission();
}
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: const Text( child: Text(
"فعالسازی", !isLocationEnabled ? "فعالسازی GPS" : "اجازه دسترسی",
style: TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
), ),
), ),
], ],
@ -111,4 +122,7 @@ Future<void> showGPSDialog(BuildContext context) async {
}, },
); );
} }
} catch (e) {
debugPrint("Error showing GPS dialog: $e");
}
} }

View File

@ -2,11 +2,9 @@ import 'dart:async';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_background_service/flutter_background_service.dart'; import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:flutter_background_service_android/flutter_background_service_android.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:proxibuy/services/mqtt_service.dart'; import 'package:proxibuy/services/mqtt_service.dart';
const notificationChannelId = 'proxibuy_foreground_service'; const notificationChannelId = 'proxibuy_foreground_service';
@ -31,15 +29,30 @@ void onStart(ServiceInstance service) async {
} }
Future<void> sendGpsData() async { Future<void> sendGpsData() async {
var locationStatus = await Permission.location.status; try {
if (!locationStatus.isGranted) { // Check location service status
debugPrint("Background Service: Location permission not granted."); bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
debugPrint("Background Service: Location service is disabled.");
return;
}
// Check location permission
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
debugPrint("Background Service: Location permission denied.");
return;
}
if (permission == LocationPermission.deniedForever) {
debugPrint("Background Service: Location permission denied forever.");
return; return;
} }
try {
final position = await Geolocator.getCurrentPosition( final position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high); locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
));
final token = await storage.read(key: 'accessToken'); final token = await storage.read(key: 'accessToken');
final userID = await storage.read(key: 'userID'); final userID = await storage.read(key: 'userID');

View File

@ -2,11 +2,9 @@ import 'dart:async';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_background_service/flutter_background_service.dart'; import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:flutter_background_service_android/flutter_background_service_android.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:proxibuy/services/mqtt_service.dart'; import 'package:proxibuy/services/mqtt_service.dart';
const notificationChannelId = 'proxibuy_foreground_service'; const notificationChannelId = 'proxibuy_foreground_service';
@ -41,14 +39,26 @@ void onStart(ServiceInstance service) async {
Timer.periodic(const Duration(seconds: 30), (timer) async { Timer.periodic(const Duration(seconds: 30), (timer) async {
debugPrint("✅ Background Service is running and sending location..."); debugPrint("✅ Background Service is running and sending location...");
var locationStatus = await Permission.location.status; try {
var locationAlwaysStatus = await Permission.locationAlways.status; // Check location service status
if (!locationStatus.isGranted || !locationAlwaysStatus.isGranted) { bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
debugPrint("Background Service: Permissions not granted. Task skipped."); if (!serviceEnabled) {
debugPrint("Background Service: Location service is disabled.");
return; return;
} }
final position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high); // Check location permission using Geolocator
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied ||
permission == LocationPermission.deniedForever) {
debugPrint("Background Service: Location permission not granted. Status: $permission");
return;
}
final position = await Geolocator.getCurrentPosition(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
));
final token = await storage.read(key: 'accessToken'); final token = await storage.read(key: 'accessToken');
final userID = await storage.read(key: 'userID'); final userID = await storage.read(key: 'userID');
@ -63,6 +73,9 @@ void onStart(ServiceInstance service) async {
debugPrint("Background Service: GPS sent successfully."); debugPrint("Background Service: GPS sent successfully.");
} }
} }
} catch (e) {
debugPrint("❌ Background Service Error: $e");
}
}); });
} }