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

Issue on Android: Not capture when app is closed, but shows notification #30

Closed
wemersonrv opened this issue Apr 4, 2020 · 31 comments
Closed

Comments

@wemersonrv
Copy link

Hello.

Testing in Android, plugin works fine, but have an issue: When close app, the notification baloon still shows on device; but stop capturing. There is some extra config to do when close app ?

Settings:

BackgroundLocator.registerLocationUpdate(
    (LocationDto locationDto) async {
        print('location in dart: ${locationDto.toString()}');
        final SendPort send = IsolateNameServer.lookupPortByName(_isolateName);
        send?.send(locationDto);
    },
    androidNotificationCallback: notificationCallback() {
        print('Background Location notificationCallback');
    },
    settings: LocationSettings(
        notificationTitle: "Notification title",
        notificationMsg: "Notification text",
        wakeLockTime: 20,
        autoStop: false,
        interval: 600, // 10 minutes interval
    ),
);
@wemersonrv wemersonrv changed the title Issue: Not capture when app is closed, but shows notification Issue on Android: Not capture when app is closed, but shows notification Apr 4, 2020
@RomanJos
Copy link
Contributor

RomanJos commented Apr 5, 2020

Look like your callback isn't static, try to create a static function like
https://github.com/rekab-app/background_locator/blob/e165b784d1ed672e11b2717c81b0bcfa2fbd122b/example/lib/main.dart#L94-L99

@wemersonrv
Copy link
Author

wemersonrv commented Apr 5, 2020

Sorry my bad, it is static... i just copy and paste it here on same code of registerLocationUpdate to make more short... but here it's static... The difference is that i'm not uising setLog

 static void callback(LocationDto locationDto) async {
    print('location in dart: ${locationDto.toString()}');
    final SendPort send = IsolateNameServer.lookupPortByName(_isolateName);
    send?.send(locationDto);
  }

Looking at topic 3 on Android item on documentation: 3. If you need to call other plugins, even when the application is terminated, create the Application.kt file and add the necessary plugins to the registerWith function.

I'm calling calling a function in another file, not in same file, and using shared prefs to store locations, and it has other plugins that i need. In that case i need to register all imported plugins on `Application.kt?

Look at imports:

lib/main.dart

port.listen(
    (dynamic data) async {
        // await updateUI(data);
        await locationBackground.updateHomeTime(data);
    },
);

lib/uteis/locator.dart

import 'dart:async';
import 'dart:isolate';
import 'dart:ui';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:geodesy/geodesy.dart';
import 'package:background_locator/location_dto.dart';
import 'package:geohash/geohash.dart';
import 'package:geoflutterfire/geoflutterfire.dart';

import './constants.dart';
import './location_model.dart';
import './uteis.dart';

class Locator{
  Uteis uteis = new Uteis();

  ReceivePort port = ReceivePort();
  static final _isolateName = 'LocatorIsolate';
  bool isRunning = true;

  static void callback(LocationDto locationDto) async {
    print('location in dart: ${locationDto.toString()}');
    final SendPort send = IsolateNameServer.lookupPortByName(_isolateName);
    send?.send(locationDto);
  }

  static void notificationCallback() {
    print('Background Location notificationCallback');
  }

  // Here is when i save the loction in shared prefs
  Future<void> updateHomeTime(LocationDto data) async {
    DateTime now = DateTime.now();
    List<LocationModel> locations = await uteis.getLocations();
    locations.add(LocationModel.fromGelocation(userPoint['email'], now, data));
  
    await uteis.storeLocations(locations);

    // TODO: Persist these locations on shared prefs to firebase firestore
  }

I am storing locations on shared prefs to send then to firebase firestore later...

@RomanJos
Copy link
Contributor

RomanJos commented Apr 6, 2020

Basically you have two part on your app, Flutter and the plugins's service
Two are in separate Isolate so they don't share no memory whatsoever, the only way of comunication is through port
on BackgroundLocator.registerLocationUpdate you pass to the plugin the callback and the notification callback (which is useless in your case and will be optional in the future), so the plugin will execute the callback at every new location every x seconds of interval you specified.
This happen in background and without the need of Flutter so the only way to change UI element is by listening through the port and do changes here.
In your case you execute what should be in callback, inside port.listen. If Flutter is killed you can't listen to anything, only the plugin Isolate will be running calling callback.

On topic 3 like you said you need to import the plugin you want to use inside the kotlin file (its path_provider on the example) so this way you can use this plugin inside the callback (Basically when you download through pub get they are auto imported insideandroid/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java, which MainActivity.kt will configureFlutterEngine using theses. On Application.kt you do the same except that you don't import them all)

Take a look here #27 you will maybe better understand (also check the example)

@oocokeoo
Copy link

oocokeoo commented Apr 6, 2020

in Android

I want to send location and another data (eg.SharedPreferences data , battery_level) to my server in background process. It works well in port.listen function.

When I kill process port.listen not working but callback still work.

I move code from port.listen function to callback function. There was some problem .
callback function is a static function therefore it can't access instance data or object (eg.SharedPreferences data , battery_level)

Can I pass some parameter to callback function or you have any idea for my situation?

@RomanJos
Copy link
Contributor

RomanJos commented Apr 6, 2020

I think you don't have imported the kotlin part of the two plugins so you get NotImplementedError

@ibrahimsoliman97
Copy link

is there any way to stop the callback when the application is killed?
I am getting the same behaviors as @oocokeoo and the notification is shown even if the app killed (without running port.listen) ,,,, but I just want to run some code in port.listen while my app in the background (not when it is killed).
So I just want to remove the notification and callback when the application is killed.

@RomanJos
Copy link
Contributor

RomanJos commented Apr 6, 2020

Then you can set autoStop to true

@ibrahimsoliman97
Copy link

@RomanJos According to the ReadMe > [autoStop] If true locator will stop as soon as app goes to background.

I have set [autoStop] to true, but once my application goes to background (without killing it), the callback stop and the notification disappear.
So, unfortunately, i am still not able to achieve what I am looking for.

@RomanJos
Copy link
Contributor

RomanJos commented Apr 6, 2020

Oh my bad I read too fast lol I think that you can achieve this by following this post https://medium.com/pharos-production/flutter-app-lifecycle-4b0ab4a4211a basically when the app is paused you stop the locator service and re-start it when the app get resumed
Its a little hacky since you use a sticky service in background to get the location only when the app is opened. Using a timer that get the location with another plugins will maybe be better idk

@ibrahimsoliman97
Copy link

Finally, I found a workaround. I think many users of this plugin looking for this use case.
Just receiving locations data when the application in the background or foreground. Then kill the services once the application has been killed by the user (force kill, swipe it from the recent application).

I achieved that by editing the 2 services from background_locator in my androidManifest.xml:
android:stopWithTask="true"

<service android:name="rekab.app.background_locator.LocatorService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true" android:stopWithTask="true"/> <service android:name="rekab.app.background_locator.IsolateHolderService" android:permission="android.permission.FOREGROUND_SERVICE" android:exported="true" android:stopWithTask="true" />

@RomanJos
Copy link
Contributor

RomanJos commented Apr 6, 2020

This is actually really clever, I don't know how android's manifest work actually I will add it in the readme

@RomanJos
Copy link
Contributor

RomanJos commented Apr 6, 2020

I think I have to take more time reading the issue, I don't know why I came up with lifecycle lol anyway

@wemersonrv
Copy link
Author

Basically you have two part on your app, Flutter and the plugins's service
Two are in separate Isolate so they don't share no memory whatsoever, the only way of comunication is through port
on BackgroundLocator.registerLocationUpdate you pass to the plugin the callback and the notification callback (which is useless in your case and will be optional in the future), so the plugin will execute the callback at every new location every x seconds of interval you specified.
This happen in background and without the need of Flutter so the only way to change UI element is by listening through the port and do changes here.
In your case you execute what should be in callback, inside port.listen. If Flutter is killed you can't listen to anything, only the plugin Isolate will be running calling callback.

On topic 3 like you said you need to import the plugin you want to use inside the kotlin file (its path_provider on the example) so this way you can use this plugin inside the callback (Basically when you download through pub get they are auto imported insideandroid/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java, which MainActivity.kt will configureFlutterEngine using theses. On Application.kt you do the same except that you don't import them all)

Take a look here #27 you will maybe better understand (also check the example)

Hi, make the Application.kt file as oriented:

package br.com.myapp

import rekab.app.background_locator.LocatorService
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.pathprovider.PathProviderPlugin

class Application : FlutterApplication(), PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        LocatorService.setPluginRegistrant(this)
    }

    override fun registerWith(registry: PluginRegistry?) {
        if (!registry!!.hasPlugin("io.flutter.plugins.pathprovider")) {
            PathProviderPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.pathprovider"))
        }
        if (!registry!!.hasPlugin("io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin")) {
            CloudFirestorePlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin"))
        }
        if (!registry!!.hasPlugin("io.flutter.plugins.firebaseauth.FirebaseAuthPlugin'")) {
            FirebaseAuthPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebaseauth.FirebaseAuthPlugin'"))
        }
        if (!registry!!.hasPlugin("com.baseflow.location_permissions.LocationPermissionsPlugin'")) {
            LocationPermissionsPlugin.registerWith(registry!!.registrarFor("com.baseflow.location_permissions.LocationPermissionsPlugin'"))
        }
        if (!registry!!.hasPlugin("io.flutter.plugins.sharedpreferences'")) {
            SharedPreferencesPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.sharedpreferences'"))
        }
    }
}

There are a couple of plugins that are installed and i use them on app but are not registered in android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java:

  • import 'package:geodesy/geodesy.dart';
  • import 'package:scoped_model/'scoped'_model.dart';
  • import 'package:uuid/uuid.dart';

Is this a proble?

@RomanJos
Copy link
Contributor

RomanJos commented Apr 6, 2020

If they are not in GeneratedPluginRegistrant.java that mean they don't have any android natives calls or specific android codes, for example generating UUID will just generate random string so you don't make any native calls where path_provider ask android the path of DocumensDirectory etc

@wemersonrv
Copy link
Author

wemersonrv commented Apr 7, 2020

If they are not in GeneratedPluginRegistrant.java that mean they don't have any android natives calls or specific android codes, for example generating UUID will just generate random string so you don't make any native calls where path_provider ask android the path of DocumensDirectory etc

Ok, understand. Making a build now, with Application.kt i posted before and it giving me the error:

D:\DadosImportantes\dev\Projetos\WemersonRV\test_background\android\app\src\main\kotlin\br\com\myapp\Application.kt: (21, 13): Unresolved reference: CloudFirestorePlugin
e: D:\DadosImportantes\dev\Projetos\WemersonRV\test_background\android\app\src\main\kotlin\br\com\myapp\Application.kt: (24, 13): Unresolved reference: FirebaseAuthPlugin
e: D:\DadosImportantes\dev\Projetos\WemersonRV\test_background\android\app\src\main\kotlin\br\com\myapp\Application.kt: (27, 13): Unresolved reference: LocationPermissionsPlugin
e: D:\DadosImportantes\dev\Projetos\WemersonRV\test_background\android\app\src\main\kotlin\br\com\myapp\Application.kt: (30, 13): Unresolved reference: SharedPreferencesPlugin

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':app:compileReleaseKotlin'.

My kotlin version in android/build.gradle is 1.3.50. Need a specific version?

So i change the kotlin file using the fixes you post in PR #27 but the error persist. See below the Application.kt final file:

package br.com.myapp

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.pathprovider.PathProviderPlugin
import io.flutter.view.FlutterMain
import rekab.app.background_locator.LocatorService

class LocationService : FlutterApplication(), PluginRegistrantCallback {
   override fun onCreate() {
        super.onCreate()
        LocatorService.setPluginRegistrant(this)
        FlutterMain.startInitialization(this)
    }

    override fun registerWith(registry: PluginRegistry?) {
        if (!registry!!.hasPlugin("io.flutter.plugins.pathprovider")) {
            PathProviderPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.pathprovider"))
        }
        if (!registry!!.hasPlugin("io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin")) {
            CloudFirestorePlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin"))
        }
        if (!registry!!.hasPlugin("io.flutter.plugins.firebaseauth.FirebaseAuthPlugin'")) {
            FirebaseAuthPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebaseauth.FirebaseAuthPlugin'"))
        }
        if (!registry!!.hasPlugin("com.baseflow.location_permissions.LocationPermissionsPlugin'")) {
            LocationPermissionsPlugin.registerWith(registry!!.registrarFor("com.baseflow.location_permissions.LocationPermissionsPlugin'"))
        }
        if (!registry!!.hasPlugin("io.flutter.plugins.sharedpreferences'")) {
            SharedPreferencesPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.sharedpreferences'"))
        }
    }
}

See the registered plugins on android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java. All plugins referenced on Application.kt are referenced:

image

@RomanJos
Copy link
Contributor

RomanJos commented Apr 7, 2020

You can't do CloudFirestorePlugin.registerWith if you don't import it before :

import io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin
import io.flutter.plugins.firebaseauth.FirebaseAuthPlugin
import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin

I don't think its a good idea to have permisison handler checking every callback but rather before launching the plugin

@mehdok
Copy link
Collaborator

mehdok commented Apr 7, 2020

Ok, understand. Making a build now, with Application.kt i posted before and it giving me the error:

I just have to ask, do you have this plugins in your podspec.yaml ?
I just added your listed plugins to my podspec.yaml and registered them without any problem.
And as @RomanJos said, you have to import the plugins first.

@wemersonrv
Copy link
Author

wemersonrv commented Apr 7, 2020

@RomanJos

You can't do CloudFirestorePlugin.registerWith if you don't import it before :

import io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin
import io.flutter.plugins.firebaseauth.FirebaseAuthPlugin
import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin

It's imported on my main.dart file.

I don't think its a good idea to have permisison handler checking every callback but rather before launching the plugin

OK, understand... It makes perfect sense, since the permission should have been resolved when you first started the app. Will remove this plugin,


@mehdok

Ok, understand. Making a build now, with Application.kt i posted before and it giving me the error:

I just have to ask, do you have this plugins in your podspec.yaml ?
I just added your listed plugins to my podspec.yaml and registered them without any problem.
And as @RomanJos said, you have to import the plugins first.
Yep, i have. Plugin works sooo fine! Amazing plugin. But if i close the app, not works.

My challenge is to make it work when user closes the app, so i need to persist data to firestore and if there is no internet connection, store it in shared prefs first.

@wemersonrv
Copy link
Author

I think i understand it now. I need to import the plugins in Application.kt before register them.

image

The documentation shows the import, but i just go to register area and forgot the imports.

@RomanJos
Copy link
Contributor

RomanJos commented Apr 7, 2020

Yeah exactly you need to import them in your kotlin files before calling their registerWith I just put the example as it is on the readme but it need to be more clear with more example, only one things that I don't understand is

if (!registry!!.hasPlugin("io.flutter.plugins.pathprovider")) {
    PathProviderPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.pathprovider"))
}

Here we check if registry (that shouldn't be null) don't have the plugin "io.flutter.plugins.pathprovider" to register it but why do we check it and don't just register it ?
And why some plugins like path provider and sharedpreference are like io.flutter.plugins.name.NamePlugin in import but io.flutter.plugins.name in hasPlugin() ?

@wemersonrv
Copy link
Author

Yeah exactly you need to import them in your kotlin files before calling their registerWith I just put the example as it is on the readme but it need to be more clear with more example, only one things that I don't understand is

if (!registry!!.hasPlugin("io.flutter.plugins.pathprovider")) {
    PathProviderPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.pathprovider"))
}

Here we check if registry (that shouldn't be null) don't have the plugin "io.flutter.plugins.pathprovider" to register it but why do we check it and don't just register it ?
And why some plugins like path provider and sharedpreference are like io.flutter.plugins.name.NamePlugin in import but io.flutter.plugins.name in hasPlugin() ?

Man, my bad... i think i understand it now... Below my last try... every plugin, has a commented line showing how it is listed on android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java

package br.com.myapp

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.pathprovider.PathProviderPlugin
import io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin
import io.flutter.plugins.firebaseauth.FirebaseAuthPlugin
import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin
import io.flutter.view.FlutterMain
import rekab.app.background_locator.LocatorService

class LocationService : FlutterApplication(), PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        LocatorService.setPluginRegistrant(this)
        FlutterMain.startInitialization(this)
    }

    override fun registerWith(registry: PluginRegistry?) {
        // flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin());
        if (!registry!!.hasPlugin("io.flutter.plugins.pathprovider")) {
            PathProviderPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.pathprovider"))
        }

        // io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin.registerWith(shimPluginRegistry.registrarFor("io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin"));
        if (!registry!!.hasPlugin("io.flutter.plugins.firebase.cloudfirestore")) {
            CloudFirestorePlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebase.cloudfirestore"))
        }

        // io.flutter.plugins.firebaseauth.FirebaseAuthPlugin.registerWith(shimPluginRegistry.registrarFor("io.flutter.plugins.firebaseauth.FirebaseAuthPlugin"));
        if (!registry!!.hasPlugin("io.flutter.plugins.firebaseauth")) {
            FirebaseAuthPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebaseauth"))
        }

        // flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin());
        if (!registry!!.hasPlugin("io.flutter.plugins.sharedpreferences")) {
            SharedPreferencesPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.sharedpreferences"))
        }
    }
}

@RomanJos
Copy link
Contributor

RomanJos commented Apr 7, 2020

Oh yeah but I forgot to tell that my last two question wasn't specifically for you I mean I didn't do this part of the readme and I don't know nothing about kotlin lol but if its working now thats perfect then

@wemersonrv
Copy link
Author

Oh yeah but I forgot to tell that my last two question wasn't specifically for you I mean I didn't do this part of the readme and I don't know nothing about kotlin lol but if its working now thats perfect then

Ok. Understand.

Well. Make a release now. App crash when start at first time.

@RomanJos
Copy link
Contributor

RomanJos commented Apr 7, 2020

App crash when start at first time.

Oh, I though it worked :( then I don't know, I tried myself with your example #30 (comment) with adding the tree import I said after and it didn't crashed on start
Maybe its when you use those plugins that it fail then I will be no help for you

@wemersonrv
Copy link
Author

wemersonrv commented Apr 7, 2020

Nope. don't working. App crash when start.

See below: Removed all plugins, and copy and paste documentation example. Just change the app package:

package br.com.myapp

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.pathprovider.PathProviderPlugin
import io.flutter.view.FlutterMain
import rekab.app.background_locator.LocatorService

class LocationService : FlutterApplication(), PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        LocatorService.setPluginRegistrant(this)
        FlutterMain.startInitialization(this)
    }
    
    override fun registerWith(registry: PluginRegistry?) {
        if (!registry!!.hasPlugin("io.flutter.plugins.pathprovider")) {
            PathProviderPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.pathprovider"))
        }
    }
}
<application
        android:name="br.com.myapp"
        android:label="My App"
        android:icon="@mipmap/ic_launcher">

But when i remove the Application.kt and change back android:name` on AndroidManifest.xml app works...

@RomanJos
Copy link
Contributor

RomanJos commented Apr 7, 2020

Ohh you forgot to add LocationService in android:name like android:name="br.com.myapp.LocationService"

@wemersonrv
Copy link
Author

wemersonrv commented Apr 7, 2020

Ohh you forgot to add LocationService in android:name like android:name="br.com.myapp.LocationService"

Yeah, right!

Changed now. App builds and run without conflict.

What i expect: Store locations to shared preferences with 6 minutes interval, whether the application is open or closed. After store in shared prefs, is internet is available; persist them to firestore too

Below a report about what is working and what's not:

  • [ OK ] Run when app is open, and device is awaked
  • [ OK ] Run when app is open, and device is locked
  • [ FALHA ] Run when app is closed, , and device is awaked
  • [ FALHA ] Run when app is closed, and device is locked

In short. Is working as before. When app is opened it works. but not when closes app. When open app, works fine again and capture every 6 minutes.

android/app/src/main/kotlink/br.copm/myapp/LocationService.kt

package br.com.myapp

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.pathprovider.PathProviderPlugin
import io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin
import io.flutter.plugins.firebaseauth.FirebaseAuthPlugin
import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin
import io.flutter.view.FlutterMain
import rekab.app.background_locator.LocatorService

class LocationService : FlutterApplication(), PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        LocatorService.setPluginRegistrant(this)
        FlutterMain.startInitialization(this)
    }

    override fun registerWith(registry: PluginRegistry?) {
        if (!registry!!.hasPlugin("io.flutter.plugins.pathprovider")) {
            PathProviderPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.pathprovider"))
        }

        if (!registry!!.hasPlugin("io.flutter.plugins.firebase.cloudfirestore")) {
            CloudFirestorePlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebase.cloudfirestore"))
        }

        if (!registry!!.hasPlugin("io.flutter.plugins.firebaseauth")) {
            FirebaseAuthPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebaseauth"))
        }

        if (!registry!!.hasPlugin("io.flutter.plugins.sharedpreferences")) {
            SharedPreferencesPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.sharedpreferences"))
        }
    }
}

android/app/src/main/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="br.com.myapp">

    <!-- App permissions -->
    <uses-permission android:name="android.permission.INTERNET"/>

    <!-- Background Locator permissions -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>


    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->

    <application
        android:name="br.com.myapp.LocationService"
        android:label="My App"
        android:icon="@mipmap/ic_launcher">

        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <!-- Facebook Login configuration -->
        <meta-data android:name="com.facebook.sdk.ApplicationId"
            android:value="@string/facebook_app_id"/>

        <activity android:name="com.facebook.FacebookActivity"
            android:configChanges=
            "keyboard|keyboardHidden|screenLayout|screenSize|orientation"
            android:label="@string/app_name" />

        <activity
        android:name="com.facebook.CustomTabActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="@string/fb_login_protocol_scheme" />
        </intent-filter>
        </activity>

        <!-- Background Locator configuration -->
        <receiver
        android:name="rekab.app.background_locator.LocatorBroadcastReceiver"
        android:enabled="true"
        android:exported="true"
        />

        <service
            android:name="rekab.app.background_locator.LocatorService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="true"
            />
        <service
            android:name="rekab.app.background_locator.IsolateHolderService"
            android:permission="android.permission.FOREGROUND_SERVICE"
            android:exported="true"
            />

        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />

    </application>
</manifest>
BackgroundLocator.registerLocationUpdate(
    LocationBackground.callback,
    androidNotificationCallback: LocationBackground.notificationCallback,
    settings: LocationSettings(
        notificationTitle: "My App",
        notificationMsg: "My Notification text",
        wakeLockTime: 20,
        autoStop: false,
        interval: 360, // 6 minutes
    ),
);

static void callback(LocationDto locationDto) async {
    print('location in dart: ${locationDto.toString()}');
    final SendPort send = IsolateNameServer.lookupPortByName(_isolateName);
    send?.send(locationDto);
  }

  static void notificationCallback() {
    print('Background Location notificationCallback');
  }

@RomanJos
Copy link
Contributor

RomanJos commented Apr 7, 2020

You need to put every background operation like writing in a file, send to internet the location etc inside the callback function and every UI operation inside the port.listen From your example it look like you only send the new location to the port in your callback function, so if Flutter get killed (like removing the app from the recent app list for example) nothing will listen to.
Also this is why we have these lines on startup https://github.com/rekab-app/background_locator/blob/02c71fa2372c03b5c33974930b9c88400993cab4/example/lib/main.dart#L34-L38
This way we remove the old port that was created from the previous Flutter Activity and assign the new one.
Don't hesitate to look at the example :
https://github.com/rekab-app/background_locator/blob/master/example/lib/main.dart

@wemersonrv
Copy link
Author

wemersonrv commented Apr 8, 2020

Hello, Now i understand! It works here. It works in a 6 minutes interval, exactly as i configured.

The unique issue is when it works with opened app, store both; from UI and from Background and i need just one!

Can i disable the ports right?

Just remove port variable declaration and port.listen? Is there more lines to remove?

// ReceivePort port = ReceivePort();
bool isRunning = true;

static final _isolateName = 'LocatorIsolate';

@override
void initState() {
    super.initState();

    if (IsolateNameServer.lookupPortByName(_isolateName) != null) {
        IsolateNameServer.removePortNameMapping(_isolateName);
    }

    /*
    IsolateNameServer.registerPortWithName(port.sendPort, _isolateName);

    port.listen(
        (dynamic data) async {
            await LocationBackground.updateHomeTime(data);
        },
        onError: (err) {
            print("Error log in dart: $err");
        },
        cancelOnError: false,
    );
    */

    initPlatformState();
}

Future<void> initPlatformState() async {
    await BackgroundLocator.initialize();
    final _isRunning = await BackgroundLocator.isRegisterLocationUpdate();

    setState(() {
        isRunning = _isRunning;
    });
}

static void callback(LocationDto locationDto) async {
    print('location in dart: ${locationDto.toString()}');

    await updateHomeTime(locationDto); // My Function

    // final SendPort send = IsolateNameServer.lookupPortByName(_isolateName);
    // send?.send(locationDto);
  }

@mehdok
Copy link
Collaborator

mehdok commented Apr 8, 2020

If you don't need UI update then remove the port. There should be no problem.

I'm closing this issue Because I think it's more like stackoverflow question.

Feel free to open it.

@mehdok mehdok closed this as completed Apr 8, 2020
@RomanJos
Copy link
Contributor

RomanJos commented Apr 8, 2020

Yeah port.listen get called only when Flutter is alive where the callback keep getting called by the plugin which is a service so it never die
and you can remove this too :

if (IsolateNameServer.lookupPortByName(_isolateName) != null) {
        IsolateNameServer.removePortNameMapping(_isolateName);
    }

it's more like stackoverflow question.

I don't think Stackoverflow is used for Flutter libraries lol

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

5 participants