codepath / android_guides

Extensive Open-Source Guides for Android Developers
guides.codepath.com
MIT License
28.29k stars 6.37k forks source link

A good example of background service getting location updates #220

Closed betorobson closed 6 years ago

betorobson commented 7 years ago

Please I really need help.

I'm facing a problem here. It is impossible to find A GOOD EXAMPLE of how create a service that must run in background and receive location updates. Also, all examples on developer.android.com are terrible, any one of them really works.

My app aim is get location updates in background, something which looks like so simple but unfortunately I could not find any good example about how to do it.

BlackBlind567 commented 5 years ago

Thanks, I found I send it back in a broadcast.

can you tell me how are you doing that I am really stuck here and I do not know how to do that for Pie devices. Thank you

betorobson commented 5 years ago

Thanks, I found I send it back in a broadcast.

can you tell me how are you doing that I am really stuck here and I do not know how to do that for Pie devices. Thank you

Are you trying to use the Passive Mode or High Accuracy geo location?

hafiz013 commented 5 years ago

First you need class LocationUpdatesBroadcastReceiver extends BroadcastReceiver. Then inside broadcast :

`public static final String ACTION_PROCESS_UPDATES = ".action" + ".PROCESS_UPDATES";

@Override
public void onReceive(Context context, Intent intent) {

    if (intent != null) {
        final String action = intent.getAction();

        if (ACTION_PROCESS_UPDATES.equals(action)) {
            Utils.setLocationUpdatesResult(context, DateFormat.getDateTimeInstance().format(new Date()));
            try {
                Utils.getLocationUpdates(context,intent,"PROCESS_UPDATES");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}`

after that, create class Utils which content static mehtod for pass value location and call notification builder.

`public static void getLocationUpdates(final Context context, final Intent intent, String broadcastevent){ NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);

    PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
            new Intent(context, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

    Notification.Builder notificationBuilder = new Notification.Builder(context)
            .setContentTitle(" ")
            .setContentText(" ")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentIntent(contentIntent)
            .setOngoing(true)
            .setStyle(new Notification.BigTextStyle().bigText(" "))
            .setAutoCancel(true);
    notificationManager.notify(3, notificationBuilder.build());

}`.

After that, in the main activity call this function:

`mFusedLocationClient.requestLocationUpdates(mLocationRequest, getPendingIntent());

    LocationRequestHelper.getInstance(getActivity()).setValue("RequestingLocationUpdates",true);

    Task<Void> task = mActivityRecognitionClient.requestActivityUpdates(
            Utils.UPDATE_INTERVAL,
            getPendingIntent());

    task.addOnSuccessListener(new OnSuccessListener<Void>() {
        @Override
        public void onSuccess(Void result) {

        }
    });
    task.addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            //Log.i(TAG, "addOnFailureListener mActivityRecognitionClient "+e);
        }
    });`

and function getPending like this;

private PendingIntent getPendingIntent() { Intent intent = new Intent(getActivity(), LocationUpdatesBroadcastReceiver.class); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); intent.setAction(LocationUpdatesBroadcastReceiver.ACTION_PROCESS_UPDATES); return PendingIntent.getBroadcast(getActivity(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); }

That's all due, and for stop update location:

` if (getPendingIntent() != null && mFusedLocationClient != null) { mFusedLocationClient.removeLocationUpdates(getPendingIntent()); }

    Utils.removeNotification(getActivity());

    if (getPendingIntent() != null && mActivityRecognitionClient != null) {
        Task<Void> task = mActivityRecognitionClient.removeActivityUpdates(
                getPendingIntent());

        task.addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
            }
        });
        task.addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {

            }
        });
    }`

function remove notification:

public static void removeNotification(Context context){ NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); notificationManager.cancelAll(); }

BlackBlind567 commented 5 years ago

Thanks sir for your great reply but here you can see my problem . Can you please shot out my problem I really need your valuable help. Thank You

BlackBlind567 commented 5 years ago

First you need class LocationUpdatesBroadcastReceiver extends BroadcastReceiver. Then inside broadcast :

`public static final String ACTION_PROCESS_UPDATES = ".action" + ".PROCESS_UPDATES";

@Override
public void onReceive(Context context, Intent intent) {

    if (intent != null) {
        final String action = intent.getAction();

        if (ACTION_PROCESS_UPDATES.equals(action)) {
            Utils.setLocationUpdatesResult(context, DateFormat.getDateTimeInstance().format(new Date()));
            try {
                Utils.getLocationUpdates(context,intent,"PROCESS_UPDATES");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}`

after that, create class Utils which content static mehtod for pass value location and call notification builder.

`public static void getLocationUpdates(final Context context, final Intent intent, String broadcastevent){ NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);

    PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
            new Intent(context, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

    Notification.Builder notificationBuilder = new Notification.Builder(context)
            .setContentTitle(" ")
            .setContentText(" ")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentIntent(contentIntent)
            .setOngoing(true)
            .setStyle(new Notification.BigTextStyle().bigText(" "))
            .setAutoCancel(true);
    notificationManager.notify(3, notificationBuilder.build());

}`.

After that, in the main activity call this function:

`mFusedLocationClient.requestLocationUpdates(mLocationRequest, getPendingIntent());

    LocationRequestHelper.getInstance(getActivity()).setValue("RequestingLocationUpdates",true);

    Task<Void> task = mActivityRecognitionClient.requestActivityUpdates(
            Utils.UPDATE_INTERVAL,
            getPendingIntent());

    task.addOnSuccessListener(new OnSuccessListener<Void>() {
        @Override
        public void onSuccess(Void result) {

        }
    });
    task.addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            //Log.i(TAG, "addOnFailureListener mActivityRecognitionClient "+e);
        }
    });`

and function getPending like this;

private PendingIntent getPendingIntent() { Intent intent = new Intent(getActivity(), LocationUpdatesBroadcastReceiver.class); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); intent.setAction(LocationUpdatesBroadcastReceiver.ACTION_PROCESS_UPDATES); return PendingIntent.getBroadcast(getActivity(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); }

That's all due, and for stop update location:

` if (getPendingIntent() != null && mFusedLocationClient != null) { mFusedLocationClient.removeLocationUpdates(getPendingIntent()); }

    Utils.removeNotification(getActivity());

    if (getPendingIntent() != null && mActivityRecognitionClient != null) {
        Task<Void> task = mActivityRecognitionClient.removeActivityUpdates(
                getPendingIntent());

        task.addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
            }
        });
        task.addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {

            }
        });
    }`

function remove notification:

public static void removeNotification(Context context){ NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); notificationManager.cancelAll(); }

Thanks sir for your great reply but here you can see my problem . Can you please shot out my problem I really need your valuable help. Thank You

hafiz013 commented 5 years ago

sorry due, in order to get location from service to be served to another activity is quite difficult and i do not know how to do it.

BlackBlind567 commented 5 years ago

I know it was late but you can find best code here

olimdzhon commented 5 years ago

I know it was late but you can find best code here

hi, i'm getting this error could you help me? MainActivity$2.onClick(MainActivity.java:99) at android.view.View.performClick(View.java:4508) at android.view.View$PerformClick.run(View.java:18675) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5590) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1280) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1096) at dalvik.system.NativeStart.main(Native Method)

BlackBlind567 commented 5 years ago

I know it was late but you can find best code here

hi, i'm getting this error could you help me? MainActivity$2.onClick(MainActivity.java:99) at android.view.View.performClick(View.java:4508) at android.view.View$PerformClick.run(View.java:18675) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5590) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1280) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1096) at dalvik.system.NativeStart.main(Native Method)

Sorry I am unable to get your problem can you describe more??

olimdzhon commented 5 years ago

I know it was late but you can find best code here

hi, i'm getting this error could you help me? MainActivity$2.onClick(MainActivity.java:99) at android.view.View.performClick(View.java:4508) at android.view.View$PerformClick.run(View.java:18675) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5590) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1280) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1096) at dalvik.system.NativeStart.main(Native Method)

Sorry I am unable to get your problem can you describe more??

I'm sorry my bad... i tried to use this code on API 19, and when you click the button app crushes

BlackBlind567 commented 5 years ago

I know it was late but you can find best code here

hi, i'm getting this error could you help me? MainActivity$2.onClick(MainActivity.java:99) at android.view.View.performClick(View.java:4508) at android.view.View$PerformClick.run(View.java:18675) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5590) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1280) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1096) at dalvik.system.NativeStart.main(Native Method)

Sorry I am unable to get your problem can you describe more??

I'm sorry my bad... i tried to use this code on API 19, and when you click the button app crushes

Pay attention here ...

  1. If problem with my repository you have to open a issue there.
  2. Bro according to my repository gradle file have min Sdk is 22 so if you want to use in 19 so change a gradle file.

Thank You !

softronetMoitri commented 5 years ago

I have used the code from SO linked above too, but how do I pass the location found back to my app, so I can update location information in Firebase? Thanks!

Can you please provide the full code?

BlackBlind567 commented 5 years ago

I have used the code from SO linked above too, but how do I pass the location found back to my app, so I can update location information in Firebase? Thanks!

Can you please provide the full code?

You can check my repository . I mentioned a link above.

GadepalliAditya1998 commented 4 years ago

run a foreground service and fetch location by location listener. Your app wont be killed and will work all the time. or Use activity recognition API to detect user movements and run location fetching code.

jordan-lumley commented 4 years ago

run a foreground service and fetch location by location listener. Your app wont be killed and will work all the time. or Use activity recognition API to detect user movements and run location fetching code.

I have done this via a foreground service. Everything works out very well. However, I am curious on if doze mode or wakelock affects foreground services? And every call that comes from the onNewLocation handler, I send the data to a restful api to store then able to view on the admin side, it is killing battery though. Any pointers?

kpradeepkumarreddy commented 4 years ago

In a foreground service, i'm using FusedLocationProviderClient to request for location updates. If the gps is turned off then i'm not getting the onLocationResult() call back. How to handle this problem ?? Before starting the service, location was on and i got a couple of callbacks in onLocationResult() method. After sometime, user turned off the gps, after that i'm not getting the onLocationResult() call back. Any idea how to deal with this problem ??

shrawanibawage commented 3 years ago

Can anybody help me out I am building an application with following specs: · This app is aimed to record trips of the user.

· Whenever user wants to go on a trip, he/she will START the trip. The app will record the location (through GPS/4G) at specific interval of time (e.g. 10 seconds, 1 minute etc, a configurable value through menu). Whenever user finishes the trip, he/she will STOP the trip.

· All trips will be saved locally on the mobile. User should be shown the list of all saved trips. Any trip should be selectable, which will fetch the saved data, in form of table.

· Next step will be, integration with GoogleMaps library. All recorded location points should be plotted on GoogleMaps view, to show entire trip (not in GoogleMaps app, but in Track-My-Trip app itself).

· GoogleMaps view can be used for live tracking of current trip, updated real time.

Following key elements should be used: -Android Studio -LocationManager APIs -data storing methods, e.g. SharedPreferences or SQLite DB -Learn Google Maps APIs -Learn Android App UI Components

Outcomes: · Recording of data for 1 trip. · List of all saved trips · Loading of data of selected trip · Plotting of Trip data on Google Maps · Live plotting of location data on Google Maps view

GadepalliAditya1998 commented 3 years ago

Hey @99002688, Should the location tracking should happen in the background or while the app is open. If you want to track while the app is open you can create an app with a Wake lock that will not turn off the screen until the user presses the power button.

With this approach, you will write a location tracking broadcast using fused location API and create a pending Intent that will generate a Broadcast and in broadcast just do what you want to do with the data or you can have callbacks too and in the locationresult callback just write the code what you want to do with the coordinates.

If needed in background even when the app is closed and removed from the activity stack, I prefer you to go with Foreground service approach. Create a Foreground service that will have Fused location API code and in the location result do what ever you want.

Note: Foreground services may be stopped in some chinese devices like Xiaomi, Realme, Oppo, Vivo etc. For those devices I went with Alarm manager approach which will trigger foreground services for every given time. (Note > 10 min) and some may work with 5 min too.

Alarm managers are vulnerable to doze mode conditions i.e. they may get delayed when device goes to doze mode. for that i have used One of the Wake lock which will turn on the screen to make device come out of doze mode. (FULL_BRIGHTNESS) .

Nahush22 commented 3 years ago

Hello @GadepalliAditya1998 . Do all Chinese manufacturers restrict location access from any kind of background activity. I tried getting location periodically in the background using normal services, foreground service & WorkManager. But each time, after 10s of the phone being locked, the location wasn't being tracked anymore.

I actually created a broadcast receiver that starts a WorkManager when powerbutton is clicked but even then while the WorkManager is started, I am not getting location data. Same happens with Foreground service.

On the other hand, turning the Battery Saver option of the app to no restrictions fixes this & now I am able to access location long(15-20 mins) after the phone has been locked.

Is WakeLock the only solution or is there any other way. Can WakeLocks be initiated from Broadcast receivers? My problem is that while the service/workmanager itself runs, unfortunately I am not getting location data.

GadepalliAditya1998 commented 3 years ago

If you see the docs of the Work manager and compare with Old API like Alarm manager, Job scheduler etc.. Work manager is suitable for the jobs that can be postponed lets say Whatsapp backup can be postponed but Image upload cannot be postponed in Facebook, so it uses Foreground service.

Since your case is to fetch location in background, You can use alarm manager to trigger some service. If your task is less than 10s, you can do everything in broadcast,but if exceeds its better to call a service. I have used foreground service so that they won't be killed easily and broadcast can call service via a Pending intent.

Wake lock will actually make your device come out of the doze mode, where device actually tries to stop all background processing to save battery. So Wake lock with Screen FULL brightness makes the device to turn on the screen and will come out of doze mode.

Nahush22 commented 3 years ago

You see...my problem isn't with Foreground service or WorkManager not running. Both run just fine in the background whenever they are called even when the device is locked. But my problem is, for some reason location access is being restricted to these services while they run in the background after the device is locked. When I turn off all battery saving features then the location data gets properly passed to the Foreground service/WorkManager & they are able to store it in Firebase.

Is there anyway to go around the battery saving feature of Chinese ROMS to get location access? Or does the battery saving feature also control doze mode & me disabling the feature also disables it? If so should I proceed with a WakeLock since I read somewhere else that Chinese roms restrict location access for even foreground services & only WakeLock fixes it?

GadepalliAditya1998 commented 3 years ago

For Stock Android like Pixel and motorola etc, You can use IgnoringBatteryOptimizations to turn off battery optimization for your app. But But... The chinese roms can impose other sorts of restrictions which may have to be turned like

In vivo it will be like: Permission manager > Advanced permission > Turn off restrict background jobs In oneplus: It will be turn off advanced battery optimizations and deep level battery optimization, those are to be turned off

I haven't come across a single generic API, that will manage these.

On Mon, Jan 25, 2021 at 1:32 PM Nahush notifications@github.com wrote:

You see...my problem isn't with Foreground service or WorkManager not running. Both run just fine in the background whenever they are called even when the device is locked. But my problem is, for some reason location access is being restricted to these services while they run in the background after the device is locked. When I turn off all battery saving features then the location data gets properly passed to the Foreground service/WorkManager & they are able to store it in Firebase.

Is there anyway to go around the battery saving feature of Chinese ROMS to get location access? Or does the battery saving feature also control doze mode & me disabling the feature also disables it? If so should I proceed with a WakeLock since I read somewhere else that Chinese roms restrict location access for even foreground services & only WakeLock fixes it?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/codepath/android_guides/issues/220#issuecomment-766627152, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMGJTW2LXLUQRF74OOCMFYDS3UQQLANCNFSM4CRJZ2FA .

HusniddinUmarov commented 3 years ago

hello who can help me google map doesn't work in the background I looked at the replies but it doesn't work

EnKattral commented 3 years ago

I am a mobile app development learner/beginner. I am in the lookout for a sample code for tracking and logging location in the background. Can you all please help me with the sample code where the MyLocationService code from this thread is used... Thank You...

IamMuhammadHasib commented 2 years ago

private fun locationEnable(): Boolean { var locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled( LocationManager.NETWORK_PROVIDER ) }

KhamdamovDilmurod commented 1 year ago

are there kotlin example?

jamshidmuxtaraliev commented 1 year ago

Hello everyone. Is there a version of the same codes written in Kotlin?