I have a Broadcast Receiver which displays a toast if GPS is put on or off. I just want to know why doesn't toast get displayed continuously as long as the GPS keep on.
please find below the code i used.
public class gpsbroad extends BroadcastReceiver implements LocationListener{
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
LocationManager mlocManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
boolean enabled = mlocManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (enabled==true) {
Toast.makeText(context, "gps on",
Toast.LENGTH_SHORT).show();
//Intent pushIntent = new Intent(context, LocalService.class);
//context.startService(pushIntent);
}
else
{
Toast.makeText(context, "gps off",
Toast.LENGTH_SHORT).show();
}
if (intent.getAction().matches("android.location.PROVIDERS_CHANGED")) {
Toast.makeText(context, "in android.location.PROVIDERS_CHANGED",
Toast.LENGTH_SHORT).show();
//Intent pushIntent = new Intent(context, LocalService.class);
//context.startService(pushIntent);
}
}
A BroadcastReceiver "wakes up" when there is an Intent it is interested in receiving. It will execute the code in onReceive() and that's it. In fact, beyond that point it may as well be destroyed as far as the system is concerned.
A BroadcastReceiver object is only valid for the duration of the call
to onReceive(Context, Intent). Once your code returns from this
function, the system considers the object to be finished and no longer
active.
A Toast can only show for a limited duration, either Toast.LENGTH_SHORT or Toast.LENGTH_LONG. In fact, those are the only durations you can specify.
Put the two together and the result is you'll see a brief message pop up on screen when you get the intent your want. In short there is nothing about your code that would make a Toast (or anything for that matter) remain on screen for as long as GPS is on. I suggest you look into NotificationManager, but I really don't see the point -- the phone will display a GPS icon in the system status bar anyway.
Related
I have created a service which finds and then stores the user's coordinates in an SQLite database.
public class GPS_Service extends Service {
DatabaseHelper myDb;
private LocationListener locationListener;
private LocationManager locationManager;
private String latitude, longitude;
#Override
public void onCreate() {
super.onCreate();
myDb = new DatabaseHelper(this);
}
#SuppressLint("MissingPermission")
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Service")
.setContentText("Coordinates Location Running")
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
Log.d("myTag", "Hello");
latitude = String.valueOf(location.getLatitude());
longitude = String.valueOf(location.getLongitude());
insertCoordinates(latitude, longitude);
Intent i = new Intent("location_update");
i.putExtra("latitude", latitude);
i.putExtra("longitude",longitude);
sendBroadcast(i);
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
};
locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 0, locationListener);
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if(locationManager != null)
locationManager.removeUpdates(locationListener);
}
private void insertCoordinates(String latitude, String longitude) {
boolean inserted = myDb.insertData(latitude, longitude); //Insert coordinates
//Check if insertion is completed
if(inserted)
Toast.makeText(GPS_Service.this, "Coordinates Inserted", Toast.LENGTH_SHORT).show();
else
Toast.makeText(GPS_Service.this, "Coordinates Not Inserted", Toast.LENGTH_SHORT).show();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I can either start or stop the service from the MainActivity like this
private void enable_buttons() {
buttonStartService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent serviceIntent = new Intent(getApplicationContext(), GPS_Service.class);
//Checks if the SDK version is higher than 26 to act accordingly
ContextCompat.startForegroundService(MainActivity.this, serviceIntent);
}
});
buttonStopService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent serviceIntent = new Intent(MainActivity.this, GPS_Service.class);
stopService(serviceIntent);
}
});
}
The problem is that when I start this service, if I either completely close the app or leave it in the background, the locationListener will work for 30 seconds and then it will stop. If I reopen the app, the service continues to work from where it stopped. Also I checked in the developer options if the service is running, and it indeed is even though the locationListener doesn't output the expected results. Any ideas?
TL;DR:
Add android:foregroundServiceType="location" to your Service's manifest entry.
EXPLANATION
This new behavior, for Android 10, is exactly as you've described: Even though you may be using a foreground service, 30 seconds after your app leaves the screen -- location updates cease.
You might've noticed that Android 10 devices present two new choices to the user when granting location permissions (for legacy (API < 29) apps, or apps that declare the ACCESS_BACKGROUND_LOCATION permission):
"Allow all the time"
"Allow only while using this app"
"Allow only while using this app" effectively means, "Allow while the app is visible onscreen". That's because the user now has the option of selectively removing location access -- even to a foreground service -- based on that criteria. Users can change this setting at any time, even if your app is running.
The Android docs explain that the solution, android:foregroundServiceType="location", was intended for your precise use case: "Google Maps"-like apps, which have a foreground service, but are expected to continue processing location data if the user switches to another app. The technique is called "continuing a user-initiated action", and it allows you to get location updates even after your app is placed in the "background".
(The docs seem to be expanding the definition of the term "background", here. In the past, if you had a foreground service, your app was considered "in the foreground" -- at least for the purposes of task priority, Doze, and so forth. Now it appears that an app is considered "in the background", with respect to location access, if it hasn't been onscreen in the last 30 seconds.)
I am not sure what UI changes (like in the Google Play store) take place when you set a particular foregroundServiceType. Regardless, it seems to me that users are unlikely to object.
OTHER SOLUTIONS FOR ANDROID 10 DEVICES
Alternatively, you could've declared a targetSdkVersion of 28 or less, which will let your app function in a location "compatibility mode".
You also have the option of gaining the ACCESS_BACKGROUND_LOCATION permission, but the docs caution against this:
If your app doesn't require location access while running in the background, it's highly recommended that you not request ACCESS_BACKGROUND_LOCATION...
This approach isn't required, for your use case, because your Activity is used to start your Service; you can be guaranteed that your app has been onscreen at least once before the Service starts getting background location updates. (At least, I assume that that is how the OS determines the start of a "user-initiated action". Presumably, the foregroundServiceType approach won't work if you're starting the Service from a JobScheduler, or a BroadcastReceiver, or something.)
PS: Hang on to that WakeLock code. You're going to need to keep the device awake, if you want to keep getting updates at a steady 10-second pace.
I dont really see any problem in the code, but I am a bit sceptical about START_NOT_STICKY. Try START_STICKY instead.
START_STICKY
If this service's process is killed while it is started (after
returning from onStartCommand(Intent, int, int)), then leave it in the
started state but don't retain this delivered intent. Later the system
will try to re-create the service. Because it is in the started state,
it will guarantee to call onStartCommand(Intent, int, int) after
creating the new service instance; if there are not any pending start
commands to be delivered to the service, it will be called with a null
intent object, so you must take care to check for this.
START_NOT_STICKY
If this service's process is killed while it is started (after
returning from onStartCommand(Intent, int, int)), and there are no new
start intents to deliver to it, then take the service out of the
started state and don't recreate until a future explicit call to
Context.startService(Intent). The service will not receive a
onStartCommand(Intent, int, int) call with a null Intent because it
will not be re-started if there are no pending Intents to deliver.
So as you are returning START_NOT_STICKY, if the process is killed, onStartCommand() will not be called again, which is where you initialize both the listener and the locationManager.
I can't seem to figure this one out. I have a broadcastReceiver that is registered in the manifest. I ping for gps once a minute, the broadcastReceiver's onReceive fires and starts a service. This service grabs a wakelock just in case, sends the GPS Coords to our server using an ASyncTask and releases the wakelock, and calls stopSelf(). This fires consistantly on my Nexus 6p and an HTC.
However on a Samsung GS5, this only works for so long. The time it stops seems random but usually within 30 mins, sometimes as short as 5 mins. The broadcastReceiver never gets called again, meaning the onReceive just stops firing.
All power saving settings on the samsung are turned off that I can notice. Unless there is a super tricky hidden one, I can't figure out how the Samsung phone can stop this broadcastReceiver, or stop GPS, whichever is happening.
This happens even if the app is not swiped closed. The phone goes idle, screen turns off, and about 5-30 mins later, the phone stops getting coords.
This happens whether I use GPS_PROVIDER or NETWORK_PROVIDER although with network provider it seems to happen even faster.
Here is where I start the GPS.
public void startBackgroundGPS () {
Activity activity = this.cordova.getActivity();
Context context = activity.getApplicationContext();
ComponentName component = new ComponentName(context, LocationReceiver.class);
int status = context.getPackageManager().getComponentEnabledSetting(component);
Log.d(TAG, Integer.toString(status));
if(status == PackageManager.COMPONENT_ENABLED_STATE_ENABLED || status == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
Log.d(TAG, "receiver is enabled");
//getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED , PackageManager.DONT_KILL_APP);
} else if(status == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
Log.d(TAG, "receiver is disabled");
context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED , PackageManager.DONT_KILL_APP);
}
Intent intent = new Intent(context, LocationReceiver.class);
intent.setAction("myBroadcast");
intent.putExtra("session_id", session_id);
intent.putExtra("device_id", device_id);
//intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
PendingIntent pendingIntent = PendingIntent.getBroadcast(activity.getApplicationContext(), 58534, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//Register for broadcast intents
locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 0, pendingIntent);
}
Here is my broadcastReceiver
public class LocationReceiver extends BroadcastReceiver {
//private static boolean semaphore = true;
#Override
public void onReceive(Context context, Intent intent) {
Log.d("DistanceFilterLocationService", intent.getAction());
Location location = (Location) intent.getExtras().get(android.location.LocationManager.KEY_LOCATION_CHANGED);
Intent locationServiceIntent = new Intent(context, DistanceFilterLocationService.class);
locationServiceIntent.putExtra("device_id", intent.getStringExtra("device_id"));
locationServiceIntent.putExtra("session_id", intent.getStringExtra("session_id"));
Double longi = location.getLongitude();
Double lati = location.getLatitude();
locationServiceIntent.putExtra("longitude", longi);
locationServiceIntent.putExtra("latitude", lati);
locationServiceIntent.putExtra("accuracy", location.getAccuracy());
locationServiceIntent.putExtra("time", location.getTime());
context.startService(locationServiceIntent);
}
}
Here is my broadcastreceiver in the manifest.
<receiver android:name="com.mypackage.LocationReceiver">
<intent-filter>
<action android:name="myBroadcast" />
</intent-filter>
</receiver>
Anyone run into something like this? Found a couple similar questions but no answers were given. This one is killing me as it completely destroys the purpose of the app on Samsung phones.
Thanks for any help.
Maybe it is not a good answer, but perhaps the android version could be the problem. Or, maybe some addons from Samsung... did you test on other Samsung phone (maybe another android version)?
I'm currently trying to get broadcast receivers running in the background of my android application, which I've been told to use an event service for. At present my broadcast receivers work fine if you're in the activity within which they're registered
private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_BATTERY_LOW)) {
Toast.makeText(arg0, "Battery's dying!!", Toast.LENGTH_LONG).show();
Log.e("LOW", "LOW");
intent = null;
}else if(intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)) {
Toast.makeText(arg0, "Battery's discharging!!", Toast.LENGTH_LONG).show();
Log.e("discharge", "discharge");
intent = null;
}else if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) {
Toast.makeText(arg0, "Battery's charging!!", Toast.LENGTH_LONG).show();
Log.e("charge", "charge");
intent = null;
}else if(intent.getAction().equals(Intent.ACTION_BATTERY_OKAY)) {
Toast.makeText(arg0, "Battery's okay!!", Toast.LENGTH_LONG).show();
Log.e("OKAY", "OKAY");
intent = null;
}
}
};
in OnCreate:
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_BATTERY_LOW));
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_BATTERY_OKAY));
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_POWER_DISCONNECTED));
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_POWER_CONNECTED));
Despite the issues that arise with leaking intentfilters by making them in oncreate, my current issue that that when I change activity these no longer run, I've read from the following: http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html#startingservices_alarmmanager
That by placing these into an IntentService and starting this service, they'll run consistently, however this involves registering the receivers in the manifest, which gives me issues in that my application is planning on allowing the user to listen out for specific events IE: these cannot be created dynamically.
Is there a way to dynamically create broadcast receivers within a class, which runs in the background of the application and is triggered when the broadcast occurs?
I want to alert the user when he is near a particular location. For this I have included a ProxmityAlert and a corresponding service in my app. But no matter what coordinates I give, it always shows me "Thank you for visiting my Area!! entering" Am I doing this the wrong way?
This is how I am doing it :
public class ProxTest extends Activity {
LocationManager lm;
double lat = 30.085514, long1 = 77.082603;
float radius = 50;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lm = (LocationManager) getSystemService(LOCATION_SERVICE);
Intent i = new Intent();
i.setAction("com.example.test.proximityalert");
PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(),
-1, i, 0);
lm.addProximityAlert(lat, long1, radius, -1, pi);
sendBroadcast(i);
System.out.println("Prox alert added ");
}
}
And this is the receiver :
public class ProximityReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
String k = LocationManager.KEY_PROXIMITY_ENTERING;
boolean state = arg1.getBooleanExtra(k, false);
if (state) {
Toast.makeText(arg0, "Welcome to my Area", Toast.LENGTH_LONG)
.show();
} else {
Toast.makeText(arg0, "Thank you for visiting my Area!!"+k,
Toast.LENGTH_LONG).show();
}
}
}
In the Manifest, I have added :
<receiver android:name="ProximityReceiver">
<intent-filter>
<action android:name="com.example.test.proximityalert">
</action>
</intent-filter>
</receiver>
In my view you should use multiple geofencing, as the simplest way to achieve what you want. You can do something like a target area. Thus the target eye is the center where you want the user to be heading off or achieving it.
(source: cision.com)
Like in this target image, for example you will have counting from outside to inside, a total of 6 geofences. The closest to the target, the higher the geofence ID 6, the most far away area from the target will be the geofence ID 1. Basically you will need to coordinate multiple geofencing to achieve what you wish. So, instead of having separated geofences with overlapping border areas, you will have centric geofences.
Each time your user gets a geofence higher value, an intent gives a signal or tone, such as: "you are getting closer". The other way around is also true, for the case the user is moving far away from the target area: "your geofence ID changed from 3 for 2, you are moving in the opposite direction from your target destination".
Your current code doesn't make this kind of distinction of different directions related to geofencing, neither shows handling of multiple geofencing. That's why the code is not behaving the way you would like it.
i ve been facing some problems trying to pass data through intents and pending intents to a BroadcastReceiver, concerning proximity alerts. More specifically, am trying to pass an object, that among others holds the user's constantly changing position. I ve tried various tactics being proposed here (and not only) but none worked, resulting to either null values or same-as-first-time created intents, when the intent is retrieved on the BroadcastReceiver's side. Tactics used:
Flagging the intent that carries the object with:FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_CLEAR_TOP+FLAG_ACTIVITY_SINGLE_TOP
Result:Null values on the BroadacastReceiver's side
Flagging the pending intent created using the initial intent, with:FLAG_UPDATE_CURRENT or FLAG_CANCEL_CURRENT
Result:Null values on the BroadacastReceiver's side
Acquiring a random ID for intent or the pending intent using System.currentTimeMillis();
Result:Intents are not fired or received at all
Nothing described above. Result:Retrieving the same initial value every time.
Code for the calling method (stripped from any experimentations/producing null values):
private void setProximityAlert(MyCar myCar) {
String locService = Context.LOCATION_SERVICE;
LocationManager locationManager;
locationManager = (LocationManager)getSystemService(locService);
float radius = myCar.getMyCarRadius();
long expiration = myCar.getMyCarExpiration();
myService.setMyDriverLat(userLat);//setting user's position
myService.setMyDriverLng(userLng);//setting user's position
Intent intent = new Intent(myCar.getMyCarName());
intent.putExtra("myCar",myCar);
PendingIntent proximityIntent = PendingIntent.getBroadcast(this, -1, intent, 0);
locationManager.addProximityAlert(myCar.getMyCarLat(), myCar.getMyCarLng(), radius, expiration, proximityIntent);
}
Code for the calling method that sets the intent filter and registers the BroadcastReceiver:
public void addNewCarPoint (MyCar myCar){
IntentFilter filter = new IntentFilter(myCar.getMyCarName());
registerReceiver(new ProximityAlertReceiver(), filter);
setProximityAlert(myCar);
}
Code for the BroadcastReceiver's side:
public class ProximityAlertReceiver extends BroadcastReceiver {
#Override
public void onReceive (Context context, Intent intent) {
MyCar myCar=(MyCar)intent.getParcelableExtra("myCar");
driverLoc=(String)Double.toString(myCar.getMyDriverLat());
Toast.makeText(context, userLoc, Toast.LENGTH_SHORT).show();
Intent i = new Intent(context, MyCarDiscoveryPrompt.class);
context.startActivity(i);//firing intent
}
public void intentDataLoader(){
}
}
Any ideas would be more than welcome.
Thank you in advance.
Hmm i think i ve found something:
I placed the BroadcastReceiver (ProximityAlerReceiver), used to detect proximity alerts in the same class (MyCarTracking.class), where the LocationListener.class is located. This,
provides immediate access to fresh location updates, creating a new intent wrapped in a new pendingIntent to be fired to the BroadcastReceiver (only when the proximity criteria are met).
flags:FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_SINGLE_TOP and FLAG_CANCEL_CURRENT on intent and pendingIntent, were kept respectively. More specifically:
Code for LocationListener:
private final LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
updateWithNewLocation(location);//update application based on new location
}
public void onProviderDisabled(String provider){
updateWithNewLocation(null);//update application if provider disabled
}
public void onProviderEnabled(String provider){
// Update application if provider enabled
}
public void onStatusChanged(String provider, int status, Bundle extras){
//update application if provider hardware status changed
}
};
Code for setProximityAlert() method:
private void setProximityAlert() {
String locService = Context.LOCATION_SERVICE;
Context context =getApplicationContext();
LocationManager locationManager;
locationManager = (LocationManager)getSystemService(locService);
float radius = myCar.getMyCarRadius();
long expiration = myCar.getMyCarExpiration();
Intent intent = new Intent(CAR_DISCOVERED);
intent.putExtra("myCar",myCar);
locationManager.getLastKnownLocation(provider);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);//flagging intent
PendingIntent proximityIntent = PendingIntent.getBroadcast(context, -1, intent, PendingIntent.FLAG_CANCEL_CURRENT);//flagging pendingIntent
locationManager.addProximityAlert(myCar.getMyCarLat(), myCar.getMyCarLng(), radius, expiration, proximityIntent);//setting proximity alert
}
This solution works producing fresh intents with fresh location updates.
Thank you all for your help and your interest :)
Try adding
intent.setData(uri);
where uri is some unique value for each pending intent
I've been struggling with this problem as well. It took me a whole night to find that a weird bug I had was related to this issue.
Here's a good discussion on google code on the subject: http://groups.google.com/group/android-developers/browse_thread/thread/b2060b27c8934921
I've solved all my problems by (ab)using both the uri in SetData and the (reserved) request code in PendingEvent.GetWhatever.
I'm also using FLAG_CANCEL_CURRENT on my intents and making sure only pendingintents that share the same purpose get the same data, action and uri.
Hope it helps a little bit.