How to Track Your Users Location in the Background in Android - Android Studio Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to a new video in this video I will show you how you can track a user's location in Android in the background using a foreground service so foreground service is usually what you use for that so you will show a notification that the app is actually active because that comes with a lot less restrictions when it comes to tracking the location of a user it also works to do this in a background service but then you need an additional permission and in my experience it's just not so common that you really need to do this without the user being aware of that so the app that we will build here is a simple UI here with a start and stop button that will well start and stop location tracking if we click this and take a look at our notifications wait a little moment then you can see here we started location tracking this is of course not a real location um this is just the the last three digits of my current location because I don't want to show you my real location so I just show you that yeah the location tracking is working here and yeah the last digits are actually changing very insignificantly so the X location stays the same but of course they're um yeah the last digits might differ a little bit and also if we minimize the app then the location tracking will still go on so let's jump into Android Studio here an empty Android super project all I did was to add these location services this permission here and I'm not permission this dependency to actually get access to the location services and everything we need to be able to track the user's location and then we can actually start to go into our manifest because we need to add a bunch of permissions here first of all we want to add a user's permission internet because the location might be fetched using the user internet or GPS we need a permission for access course location which is used to fetch the user's rough location so it's a little bit inaccurate and we want to have access find location which is used in combination with that if you also want to fetch the users yeah more exact location and one more permission here is actually foreground service since we want to use a foreground service here a service in Android that pretty much just runs in the background but the user is actively aware of that by yeah that we just show a notification so now that we have all of our permissions we can close this manifest again and you wouldn't be on my channel if I wouldn't show you how to also properly integrate that here into an existing architecture in a way that scales because I think otherwise that might still be hard for some of you if you want to actually build something real with what you learn here but you don't really know how to integrate that into your architecture and that's why I will show you a cool abstraction that I typically like to use when I Implement location tracking so we will build an interface here that abstracts out the these location updates and I will call that location client make that an interface and in here we simply have a function get location updates we're going to be able to pass an interval so how often we want an update interval and this will return a flow from coroutine's flow here of type location so this will effectively give us a flow that will have an emission that will emit a location every time there is a new location that we Fetch and we also want to have a little class here which I will call location exception if something goes wrong for example if the user does not have location permission or if GPS disabled or something similar so here we want to pass a message and that's just a normal exception cool and that's already everything for this abstraction so we can use this without actually giving away any information about implementation details so you could also easily use this in a view model Because the actual implementation will need the context which you don't want to use in the viewmodel but since yeah you have an abstraction and then an implementation of that you would also easily just fetch these location updates in the view model and update some kind of state or so so it's really convenient easy to deal with let's go to our root package and I'll write the actual implementation of this location client I will call this default location client make that a class and it will Implement our interface that we just declared it will take two Constructor parameters here on the one hand as I said at the context and on the other hand it will use a client which is a fuse location provider client that's a dependency that comes from our Gradle dependency that we included and it's used to actually get the user's location so that's the actual Android specific implementation of the location client that we can use to actually get the user's location and in here we can say Ctrl I implement this get location updates function and here we of course now need to return the flow and the first thing you want to do here is to check if the user actually accepted location permission because these are considered dangerous permissions so the user explicitly needs to accept these so it's not as easy as with just adding in Internet permission that we don't need to get some users approval or so but yeah we actually need to show a permission dialog and then asks the user to actually Grant access or give our app the permission to fetch their location so what we want to do is we want to actually go to our root package and create little context util context extension package file and rewrite an extension function for context has location permission return Boolean and here we can return context combat check self permission pass this as the context and the permission you're going to check is manifest permission access course location and if that's equal to package manager permission granted oops we know that we have this permission but we also want to check for the other one which is access find location so we put this here let's swap this out with access find location and then we're good now we have a function to easily check that in our location client and what we want to return here is not just a normal flow like this now we want to return a callback flow a callback flow is used if you actually have a callback that you want to transform into your flow so you have some callback that gets fired over and over again like our location callback that we will have here that gets triggered when we fetch a new location and we want that as a float because flows are just cruel to deal with and very convenient so we use a callback flow and the Callback flow is also very useful if we actually want to model some kind of callback that has a life cycle so for these location updates that's the case because we need to tell our location client this fuse location provider client hey now start to fetch location update and then there's also a function to actually say okay please now stop to fetch the user's location and with this callback flow we can very easily kind of model this Behavior using core routines so that we can collect this flow but as soon as we stop collecting this for example when yeah the the scope is canceled when the user navigates away and view model scope is canceled or so then we can automatically call this function to stop getting location updates you will see how convenient and nice that is first of all I want to check if context has location promotion if that's false we want to throw an exception our location client dot location exception and say okay um missing location permission if that is the case if the user has location permission the next thing I want to check is if they actually are able to fetch their location so if they have GPS enabled or the network provider so to fetch the location using the user's Network we can do this using the system service location manager which we can get like this uh context.getsystem service context.location service and we cast it as a location manager we can then say Val has GPS or let's say is GPS enabled and that's equal to location manager is provider enabled location manager GPS provider we do the same for is Network enabled we simply swap out GPS provider with a network provider and if both these options are false so if if we don't have GPS enabled and we don't have the network provider enabled we also want to throw an exception so through location client location exception and we say GPS is disabled cool so at this point we can be sure that we can actually fetch the user's location so what's next next up we need to create a so-called location request where we just Define hey this is how often we want to fetch the user location this is how accurate we want it to be and just some further configuration so Val W request is location request from Android location here I think at least and we say Builder actually not I think it is the the other one from this one com Google Android GMS location we say that create yes that's the one and now we have a typical Builder pattern where we can say set interval which is our interval we passed set fastest interval so there could be a little bit of uh difference or these intervals could differ a little bit I mean so this is the slowest interval or the the average interval you can say and there is a fastest interval where you can say okay it should really not be faster than this one I will just pass the same here which will later on set to 10 seconds or so we can then also say um do we have accuracy not sure um I actually didn't do that in my preparations doesn't matter we can set the priority we could also just say build or create oh no we actually don't we already get a location request here so we don't need to do anything and now we can use this location request we built and we can say we create a callback so location callback is equal to object location callback and here we implement the on location result function this function will now be called whenever our fuse location provider client fetches a new location so we want to rename this with result and this result now contains a locations list and the last element of that locations list is actually the new location that was fetched so locations that last or no if that exists so if there is a last entry which is our location we actually want to notify our flow and send something in that flow so that we get that location that we just fetched we can do this by using launch and then call send since then we can send a location here in our colleague flow yes simply the location we just fetched yeah and that's how we specify a location callback this will now be called for every single location we Fetch and we of course right now don't do anything with that callback and that's what we will use our location client for so location or how did I call it just client a DOT request location updates the location request is our request the Callback is our location callback and we need to pass a Looper as you can see we just Looper get main Looper and we do get a little error here because it doesn't recognize that we actually check for the permission here in our extension function so we can just get rid of that Warning by pressing Alt Enter and adding the suppress lint annotation and then we get rid of that warning and now all we really need to do is since we are inside of callback flow and as I said we want to use this if we have some kind of callback with a life cycle since here we start to request location update and when the flow actually stops so when we stop collecting we automatically also want to say that we stop getting the location updates so what we can say is we can say a weight close which will block the flow until it's closed until the correcting scope is canceled or the job is running in and in here we can say client remove location updates and we pass our location callback again and that's effectively how we can transform our location callback into a very convenient class with a single responsibility with a nice abstraction which we can now use wherever we like in our code the next step is to actually set up our service because we of course now want to fetch the location not just in the foreground but also in the background or rather only in the background in this tutorial and for that in Android we use a service which simply keeps on running when we minimize our app and especially if we make it a vlogrand service which comes with a notification so the user just knows that there is a service running then the app is still kind of considered in the foreground that's why it's called like that and therefore we don't have these typical restrictions we have with location tracking in the background as we have with the normal Services because if you want to do that in normal service so the user is not aware of that then you would need to add an additional permission which is access background location this one would be needed for I think at least API level something I forgot it but you need this and you also need to request this this I think the user actually needs to go to the app settings and toggle this on and Google Play will also only approve it if they see a legitimate reason in actually fetching the user location in the background without them knowing about that so we don't want to do this for that reason usually you're fine with using a foreground service for example if you want to build a running Tracker app which I also have on my YouTube channel then the user of course knows when they are currently running and they know that it's that it's normal that they AB track their location while doing so and then the notification would simply show currently tracking your run or so so they will know about that let's create our foreground service here called that location service and make that inherit from service and then we will need to implement this on bind function which we can simply return null in since we don't bind our service to anything and let's then set up some helper variables on the one hand we want to have a curtain scope I will Implement a service scope here which is simply bound to the lifetime of our service and we can create that using curtainscope supervise a job plus dispatches IO since we deal with IO things here in our location service tracking the location is an i o operation in my opinion not just an open unit is like that and using supervisor job just make sure that if one job here in this scope fails then the others will still keep running which is of course what we want in this context and we want to have a private light and a variable location client which is our location client abstraction we just created since then in oncreate when our service is created we can say location client is equal to our default location client and usually I would use something like di here but yeah just for the sake of Simplicity I don't do this in this tutorial um here we want to say the context is just this or we can pass the application context and the client is a location services get Fused location provider client and we also pass our application context here and the way this now works if we want to communicate with this service so if we have our activity and we click our start and stop button we of course need to send some information to our service to update our notification or to just start the tracking to do that we want to Define two actions and a companion object action start which we Center the service if we want to start the tracking and we want to have action stop which we call what we want to stop this tracking and then there's a function for services called onstart command where we can check what the action actually is so this is now called for every single intent we send to the service and if these intents actually have an action so when intent that action is action start then we want to start our service and if it's action stop we want to stop our service and those are two functions we will now Implement on the one hand private function start then we want to have a private function stop and we actually also want to override on Destroy so when the service is destroyed in that case we want to make sure that we cancel our creatine scope and when we cancel this since we now use a callback flow in our default location client and we launch this flow in our serverscope later on then as soon as we cancel the scope so as soon as the service is destroyed we automatically also stop tracking the location so there can't be any weird leaks or so because you forget to actually call this remove location updates function this really can't happen with this nice abstraction we implemented here so let's start to implement our start function and as I said since we want to use a foreground service here which comes with a notification we of course we need to show this notification and create it first so what I want to do is I want to have a notification is equal to notification combat dot Builder we pass our contacts and we pass a channel ID which I will just choose location here we want to set a Content title which is the title of or notification I will say a tracking location we want to set a Content text which I will say okay the location is actually let's just say no by default so we don't have a location initially of course we want to say set small icon that's what a notification needs I will just set it to the launcher background just a green square doesn't matter here and we say set ongoing to true so we just can't swipe it away and we then want to say start foreground to yeah just make sure to make this a service a foreground service we want to pass an ID make sure to pass at least one here and for the notification we say notification dot build however that of course now did not start the location tracking and actually updates our location so we don't see this null here how do we do that instead well we of course now want to also launch our callback flow together with a start function so right here we want to say location client get location updates the interval let's say is 10 seconds so every 10 seconds we get a location update and we can say first of all catch so if we get any exceptions we could print these or simply say um either print stack Trace and then we can say on each location that we get here we simply want to update our notification of course so we want to say or at least I want to say you don't need to do that here my LED is simply location latitude choose string take last three so again I just take the three last digits of my location so you don't see my whole location and I'll do the same with the longitude again you can simply use location.latitude and longitude here directly and you will get the full location of course and then we can say we actually construct an updated notification it's equal to our notification dot a set content text and here we simply say um location is Led long just like that and we can then say we actually get a reference to our notification manager up here which is get system service context oops notification Service as a notification manager and we can now use that to actually update our existing notifications by simply calling notification manager that notify with the same ID we chose below and we pass updated notification.build cool so that's really what we want to do on every single location update we of course now need to make sure that we launch this in our service scope and that will effectively bind this callback flow this function returns to the lifetime of our service since we cancel our service scope in on Destroy and that should be it for the start function we now need to implement the on stock function which is very simple we can simply say on the one hand stop foreground pass through to make sure we remove the Nullification when this is stopped then calling stop self to actually stop the service and that's pretty much it cool so now that we have our service ready we want to go to our manifest since we actually need to register our service here we want to say service location service and what is very important now is that we say foreground service type and we set this to location otherwise Android won't allow you to actually track the user's location in the foreground service if you don't actually explicitly mention this I've had a lot of pain with that in the past that I didn't add this and it didn't work but with that it works and we now want to create an application class since if we want to show notification we also need to create a notification channel in which we send these notifications so we say location app for example make that an application and here on create we can create our notification Channel and we only need to do that from Android Oreo onwards so we've built version SDK and is greater than or equal to build.version codes o then we want to do this and we say about Channel is a notification Channel the idea is the same as we actually chose here in our location service I'm just too lazy now to make it a constant so just make sure you have the same as you mentioned here then the name is location as well for example and the importance is notification manager important I will choose low here you can choose High then it will appear higher at like at a higher rank in your notifications but this will also come with notification sounds so every time there is a new notification update this will make a sound you can disable that but um I won't do that here I'll just choose low and then we only get a reference to our notification manager so again we can say get system service context notification Service as a notification manager and we can then say notification manager create notification Channel and we pass our channel here we then go back to our manifest and we make sure to register this application class here under the name attribute and add this location app cool the last thing we need to do is to go to main activity and actually add our two buttons and make our permission request I will do the easiest permission request possible here so we just assume the user accepts these permissions and grants them of course if you have a real app then you need to do some more fine-grained permission handling here and also handle cases where the user declines that I won't cover this here because I don't think it's really necessary for the sake of this tutorial if you want to learn about location tracking so we can just say Activity combat request permissions pass this activity has an array of permissions you want to request and that's simply manifest permission access course location and access find location and the request code is simply zero here and then here in our compose UI we can add a column let's say we make that fill our whole size and here we add two buttons that's the first button we can give it a text of start adding a little bit of space in between let's say six into p and then we have the same button down below and just Swap this out with stop cool so now the last thing we need to do is to actually fill in these on click listeners and what do we want to do here we of course want to launch our service and we simply do that by sending an intent to our service so with that we trigger this onstart command function with an intent that has the action start or stop depending on what we have to do so we say intent pass the application context and pass the service class location service class at Java and we can say apply and we say the action is location service action start and then we simply do the same for our own stop button and simply Swap this out with action stop and of course you also need to say start service to actually send this command to the service by saying this and here actually as well and don't confuse this here because this is action stop and this is start service we the start service function will actually just send this intent to the service and it will then check okay it's actually action stop and I will yeah well the service will then call this stop function here if we launch this and try this out check that here we first of all want to say while using the app to Grant permission we could have centered these of course um but let's not care about that now clicking start and waiting a little moment maybe it doesn't work now but after relaunching the app it always worked I'm actually not sure why um also why there's sometimes a little delay okay like like right now it works you can see the notification is actually showing it is tracking my location and showing that in the notification as well and that will now also work if we actually minimize the app take a look here then this will actually keep on running and running and running a program service is pretty much an infinite service so until you stop it at least for some Chinese devices there yeah some of these live to kill foreground services in some rather rare circumstances but usually a foreground service is used to keep it running as long as you want if this video helped you to actually get better at Android to learn about this topic and you like this then consider subscribing to this channel because then you will get two of such videos every single week and maybe we can even reach the hundred thousand subscribers at the end of this year which would be amazing and that would be very very happy about that so if you're not a subscriber yet do subscribe now and I wish you an excellent rest of the week and enjoy your day and see you back in the next video bye bye
Info
Channel: Philipp Lackner
Views: 72,899
Rating: undefined out of 5
Keywords:
Id: Jj14sw4Yxk0
Channel Id: undefined
Length: 28min 20sec (1700 seconds)
Published: Wed Sep 28 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.