Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question]: #1496

Open
3 of 8 tasks
grecky-goo opened this issue May 2, 2024 · 2 comments
Open
3 of 8 tasks

[Question]: #1496

grecky-goo opened this issue May 2, 2024 · 2 comments

Comments

@grecky-goo
Copy link

grecky-goo commented May 2, 2024

Please check the following before submitting a new issue.

Please select for which platform(s) you need help

  • Android
  • iOS
  • Linux
  • macOS
  • Web
  • Windows

Your question

This code works on a Pixel 6, but not on a Samsung phone. I've tried a couple of Samsung phones running Android 13 and 14. They work the same. While running a Foreground Service, the code works when the app is in the foreground, when the app in in the background the geolocator engine detaches. When the app returns to the to the foreground the geolocator updates continue. What am I doing wrong?

D/FlutterGeolocator(16946): Detaching Geolocator from activity
D/FlutterGeolocator(16946): Flutter engine disconnected. Connected engine count 1
D/FlutterGeolocator(16946): Disposing Geolocator services
E/FlutterGeolocator(16946): Geolocator position updates stopped

final position = await Geolocator.getCurrentPosition(
            desiredAccuracy: LocationAccuracy.best,
            forceAndroidLocationManager: true,
            timeLimit: const Duration(minutes: 5),
);

on line 163 never returns, or since I added the timeout it gets a timeout.

import 'dart:async';
import 'dart:io';
import 'dart:ui';

import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.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:fluttertoast/fluttertoast.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'package:shared_preferences/shared_preferences.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await requestLocationPermissions();
  await initializeService();
  runApp(const MyApp());
}

Future<void> initializeService() async {
  final service = FlutterBackgroundService();

  /// OPTIONAL, using custom notification channel id
  const AndroidNotificationChannel channel = AndroidNotificationChannel(
    'rendezvous_service', // id
    'command channel', // title
    description:
        'This channel is used for important notifications.', // description
    importance: Importance.low, // importance must be at low or higher level
  );

  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();

  if (Platform.isIOS || Platform.isAndroid) {
    await flutterLocalNotificationsPlugin.initialize(
      const InitializationSettings(
        iOS: DarwinInitializationSettings(),
        android: AndroidInitializationSettings('ic_bg_service_small'),
      ),
    );
  }

  await flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin>()
      ?.createNotificationChannel(channel);

  await service.configure(
    // THis service will be in the foreground
    androidConfiguration: AndroidConfiguration(
      // this will be executed when app is in foreground or background in separated isolate
      onStart: onStart,
      autoStart: true,
      isForegroundMode: true,
      notificationChannelId: 'rendezvous_service',
      initialNotificationTitle: 'AWESOME SERVICE',
      initialNotificationContent: 'Initializing',
      foregroundServiceNotificationId: 888,
    ),
    iosConfiguration: IosConfiguration(
      // auto start service
      autoStart: true,

      // this will be executed when app is in foreground in separated isolate
      onForeground: onStart,

      // you have to enable background fetch capability on xcode project
      onBackground: onIosBackground,
    ),
  );
}

// to ensure this is executed
// run app from xcode, then from xcode menu, select Simulate Background Fetch

@pragma('vm:entry-point')
Future<bool> onIosBackground(ServiceInstance service) async {
  WidgetsFlutterBinding.ensureInitialized();
  DartPluginRegistrant.ensureInitialized();

  SharedPreferences preferences = await SharedPreferences.getInstance();
  await preferences.reload();
  final log = preferences.getStringList('log') ?? <String>[];
  log.add(DateTime.now().toIso8601String());
  await preferences.setStringList('log', log);

  return true;
}

@pragma('vm:entry-point')
void onStart(ServiceInstance service) async {
  // Only available for flutter 3.0.0 and later
  DartPluginRegistrant.ensureInitialized();
  SharedPreferences preferences = await SharedPreferences.getInstance();
  await preferences.setString("hello", "world");

  /// OPTIONAL when use custom notification
  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();
  if (service is AndroidServiceInstance) {
    service.on('setAsForeground').listen((event) {
      service.setAsForegroundService();
    });
    service.on('setAsBackground').listen((event) {
      service.setAsBackgroundService();
    });
  }
  service.on('stopService').listen((event) {
    service.stopSelf();
  });
  Timer.periodic(const Duration(seconds: 30), (timer) async {
    try {
      if (service is AndroidServiceInstance) {
        /// Per Android policy, a foreground service is required to provide
        /// persistent notifications for transparency to the user.
        /// Is monitored by system resource management. If not done, the service
        /// will be terminated
        if (await service.isForegroundService()) {
          flutterLocalNotificationsPlugin.show(
            888,
            'Firebase Update',
            'Location Updated At ${DateTime.now()}',
            const NotificationDetails(
              android: AndroidNotificationDetails(
                'rendezvous_service',
                'COMMANDs',
                icon: 'ic_bg_service_small',
                ongoing: true,
              ),
            ),
          );
          // if you don't using custom notification, uncomment this
          service.setForegroundNotificationInfo(
            title: "My App Service",
            content: "Updated at ${DateTime.now()}",
          );
        }
        try {
          // Get current location
          print('******************* updateLocation');
          bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
          if (!serviceEnabled) {
            Fluttertoast.showToast(msg: "Location services are not enabled");
            return Future.error('Location services are not enabled');
          }
          LocationPermission permission = await Geolocator.checkPermission();
          if (permission == LocationPermission.denied) {
            //permission = await _geolocatorPlatform.requestPermission();
            permission = await Geolocator.requestPermission();
            if (permission == LocationPermission.denied) {
              Fluttertoast.showToast(msg: "Location permission denied");
              return Future.error("Location permission denied by user");
            }
          }
          if (permission == LocationPermission.deniedForever) {
            Fluttertoast.showToast(msg: "Location permission denied forever");
            return Future.error("Location permission permanently denied");
          }
          print('past permissions calling getposition');
          final position = await Geolocator.getCurrentPosition(
            desiredAccuracy: LocationAccuracy.best,
            forceAndroidLocationManager: true,
            timeLimit: const Duration(minutes: 5),
          );
          print('past getCurrentPosition: $position');
          String address = '';
          // geocoding only runs on android.  Get the address.
          List<Placemark> placemarks = await placemarkFromCoordinates(
              position.latitude, position.longitude);
          if (placemarks.isNotEmpty) {
            Placemark placemark = placemarks[0];
            address =
                "${placemark.street}, ${placemark.locality}, ${placemark.administrativeArea}, ${placemark.country}";
          }
          print('updateLocation position:$position address: $address');
        } on Exception catch (e) {
          print('update_location exception:\n $e');
        }

        /// you can see this log in logcat
        if (await service.isForegroundService()) {
          print('FLUTTER FOREGROUND SERVICE: ${DateTime.now()}');
        } else {
          print('FLUTTER BACKGROUND SERVICE: ${DateTime.now()}');
        }
        // test using external plugin
        final deviceInfo = DeviceInfoPlugin();
        String? device;
        if (Platform.isAndroid) {
          final androidInfo = await deviceInfo.androidInfo;
          device = androidInfo.model;
        }

        if (Platform.isIOS) {
          final iosInfo = await deviceInfo.iosInfo;
          device = iosInfo.model;
        }

        service.invoke(
          'update',
          {
            "current_date": DateTime.now().toIso8601String(),
            "device": device,
          },
        );
      }
    } on Exception catch (e) {
      print('Service exception:\n $e');
    }
  });
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String text = "Stop Service";
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Service App'),
        ),
        body: Column(
          children: [
            StreamBuilder<Map<String, dynamic>?>(
              stream: FlutterBackgroundService().on('update'),
              builder: (context, snapshot) {
                if (!snapshot.hasData) {
                  return const Center(
                    child: CircularProgressIndicator(),
                  );
                }

                final data = snapshot.data!;
                String? device = data["device"];
                DateTime? date = DateTime.tryParse(data["current_date"]);
                return Column(
                  children: [
                    Text(device ?? 'Unknown'),
                    Text(date.toString()),
                  ],
                );
              },
            ),
            ElevatedButton(
              child: const Text("Foreground Mode"),
              onPressed: () {
                FlutterBackgroundService().invoke("setAsForeground");
              },
            ),
            ElevatedButton(
              child: const Text("Background Mode"),
              onPressed: () {
                FlutterBackgroundService().invoke("setAsBackground");
              },
            ),
            ElevatedButton(
              child: Text(text),
              onPressed: () async {
                final service = FlutterBackgroundService();
                var isRunning = await service.isRunning();
                if (isRunning) {
                  service.invoke("stopService");
                } else {
                  service.startService();
                }

                if (!isRunning) {
                  text = 'Stop Service';
                } else {
                  text = 'Start Service';
                }
                setState(() {});
              },
            ),
            // const Expanded(
            //   child: LogView(),
            // ),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {},
          child: const Icon(Icons.play_arrow),
        ),
      ),
    );
  }
}


Future<void> requestLocationPermissions() async {
  bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
  if (!serviceEnabled) {
    print('location service is not enabled');
    Fluttertoast.showToast(msg: "Location services are not enabled");
    return Future.error('Location services are not enabled');
  }
  LocationPermission permission = await Geolocator.checkPermission();
  if (permission == LocationPermission.denied) {
    //permission = await _geolocatorPlatform.requestPermission();
    permission = await Geolocator.requestPermission();
    if (permission == LocationPermission.denied) {
      print('request permissions');
      Fluttertoast.showToast(msg: "Location permission denied");
      return Future.error("Location permission denied by user");
    }
  }
  if (permission == LocationPermission.deniedForever) {
    print('location denied forever');
    Fluttertoast.showToast(msg: "Location permission denied forever");
    return Future.error("Location permission permanently denied");
  }
}

Version

^11.0.0

@dmiedev
Copy link

dmiedev commented May 12, 2024

From docs:

Starting from Android 10 you need to add the ACCESS_BACKGROUND_LOCATION permission (next to the ACCESS_COARSE_LOCATION or the ACCESS_FINE_LOCATION permission) if you want to continue receiving updates even when your App is running in the background (note that the geolocator plugin doesn't support receiving and processing location updates while running in the background):

@nitinkhanduri
Copy link

I am Facing same issue
Geolocator.getPositionStream().listen((Position position) {
currentPosition = position;
myPostion = position;
LatLng mPostion = LatLng(position.latitude, position.longitude);
when app is in background in samsung update version 1 april 2024 when app is in background then location is
not fetch only work in foreground only new latest version it is not working

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants