Hey I m going to develop an location tracker app in which, this app in client device which constantly send it location to the firebase db.
Here the problem is that it will send the data to firebase only first 3 minutes then it wont. I don't know whats happening. ?
For that even i put a log message that log message is printed perfectly even after three minutes
Any one please help on this........!
Here i attached 3 file One BackgroundLocation: Which is the service in background which will extract the device location and call the LocationReceiver which extends broadcast receiver where it will print log message and send the data to firebase through FBSender.
Thanks in advance
BackgroundLocation.java
Which runs in Background to get the location details and call the broadcast Reveiver. LocationReveiver.java
/**
* Created by geekyint on 1/7/16.
*/
public class BackgroundLocation extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
IBinder mBinder = new LocalBinder();
private GoogleApiClient mGoogleApiClient;
private PowerManager.WakeLock mWakeLock;
private LocationRequest mlocationRequest;
//Flag for boolean request
private boolean mInProgress;
private boolean serviceAvailabe = false;
public class LocalBinder extends Binder {
public BackgroundLocation getServerInstance() {
return BackgroundLocation.this;
}
}
#Override
public void onCreate() {
super.onCreate();
mInProgress = false;
//Create the lcoation request object
mlocationRequest = LocationRequest.create();
//Use the acurecy
mlocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
//The INTERVAL
mlocationRequest.setInterval(Constants.UPDATE_INTERVAL);
//The FAST INTERVAL
mlocationRequest.setFastestInterval(Constants.FAST_INTERVAL);
serviceAvailabe = serviceConnected();
setUpALocationClientIfNeeded();
ComponentName receiver = new ComponentName(this, LocationReceiver.class);
PackageManager pm = this.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
/*ComponentName receiver1 = new ComponentName(this, FireBaseSender.class);
PackageManager pm1 = this.getPackageManager();
pm1.setComponentEnabledSetting(receiver1,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);*/
}
private void setUpALocationClientIfNeeded() {
if (mGoogleApiClient == null) {
buildGoogleApiClient();
}
}
//Create the new Connection to the client
private void buildGoogleApiClient() {
this.mGoogleApiClient = new GoogleApiClient.Builder(this)
.addOnConnectionFailedListener(this)
.addConnectionCallbacks(this)
.addApi(LocationServices.API)
.build();
}
private boolean serviceConnected() {
//Check the google Play service availibility
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
//IF AVAILABLE
if (resultCode == ConnectionResult.SUCCESS) {
return true;
} else {
return false;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (this.mWakeLock == null) {
this.mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
}
if (!this.mWakeLock.isHeld()) {
this.mWakeLock.acquire();
}
if (!serviceAvailabe || mGoogleApiClient.isConnected() || mInProgress) {
return START_STICKY;
}
setUpALocationClientIfNeeded();
if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() || !mInProgress) {
// appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Started", Constants.LOG_FILE);
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
#Override
public void onLocationChanged(Location location) {
String msg = Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Log.d("debug", msg);
// Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
// appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ":" + msg, Constants.LOCATION_FILE);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public String getTime() {
SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return mDateFormat.format(new Date());
}
public void appendLog(String text, String filename) {
File logFile = new File(filename);
if (!logFile.exists()) {
try {
logFile.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
//BufferedWriter for performance, true to set append to file flag
BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
buf.append(text);
buf.newLine();
buf.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void onDestroy() {
// Turn off the request flag
this.mInProgress = false;
if (this.serviceAvailabe && this.mGoogleApiClient != null) {
this.mGoogleApiClient.unregisterConnectionCallbacks(this);
this.mGoogleApiClient.unregisterConnectionFailedListener(this);
this.mGoogleApiClient.disconnect();
// Destroy the current location client
this.mGoogleApiClient = null;
}
// Display the connection status
// Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ":
// Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
if (this.mWakeLock != null) {
this.mWakeLock.release();
this.mWakeLock = null;
}
// appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Stopped", Constants.LOG_FILE);
ComponentName receiver = new ComponentName(this, LocationReceiver.class);
PackageManager pm = this.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
/*
ComponentName receiver1 = new ComponentName(this, FireBaseSender.class);
PackageManager pm1 = this.getPackageManager();
pm1.setComponentEnabledSetting(receiver1,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);*/
super.onDestroy();
}
/*
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
#Override
public void onConnected(Bundle bundle) {
// Request location updates using static settings
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
Intent intent = new Intent (this, LocationReceiver.class);
PendingIntent pendingIntent = PendingIntent
.getBroadcast(this, 54321, intent, PendingIntent.FLAG_CANCEL_CURRENT);
LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient,
mlocationRequest, pendingIntent);
}
/*
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
#Override
public void onConnectionSuspended(int i) {
// Turn off the request flag
mInProgress = false;
// Destroy the current location client
mGoogleApiClient = null;
// Display the connection status
// Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ": Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
// appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Disconnected", Constants.LOG_FILE);
}
/*
* Called by Location Services if the attempt to
* Location Services fails.
*/
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
mInProgress = false;
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
// If no resolution is available, display an error dialog
} else {
}
}
}
Here The LocationReceiver Code:
public class LocationReceiver extends BroadcastReceiver {
private String TAG = this.getClass().getSimpleName();
private LocationResult mLocationResult;
private double latitude;
private double longitude;
private double speed;
private String time;
#Override
public void onReceive(Context context, Intent intent) {
// Need to check and grab the Intent's extras like so
if(LocationResult.hasResult(intent)) {
this.mLocationResult = LocationResult.extractResult(intent);
//new SaveToFireB().insertToFireBase(mLocationResult.getLastLocation());
new FBSender().put(mLocationResult.getLastLocation());
Log.i(TAG, "Location Received: " + this.mLocationResult.toString());
String msg = String.valueOf(mLocationResult.getLastLocation().getLongitude()) + " " +
String.valueOf(mLocationResult.getLastLocation().getLatitude());
// appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ":" + msg, Constants.LOCATION_FILE);
}
}
public void appendLog(String text, String filename) {
File logFile = new File(filename);
if (!logFile.exists()) {
try {
logFile.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
//BufferedWriter for performance, true to set append to file flag
BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
buf.append(text);
buf.newLine();
buf.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Here Which will call FBSender to send the data to firebase.
Ther real problems comes here.
It will send the data only in first three minutes then it wont send the data to firebase
For confirmation whether the control going there or not i put log message there that log message will be printed perfectly even after 3 minutes from the start of the app
Here is FBSender.Java
public class FBSender extends Service {
private String TAG = "FBSender";
private double latitude;
private double longitude;
private double speed;
private String time;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
public void put (Location location) {
latitude = location.getLatitude();
longitude = location.getLongitude();
speed = location.getSpeed();
time = DateFormat.getTimeInstance().format(new Date());
Log.e(TAG, "Entering the run ()");
final FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference reference = database.getReference("users/" + user.getUid() + "/vehicles");
Log.e(TAG, "I M in the middle");
Map mLocations = new HashMap();
mLocations.put("latitude", latitude);
mLocations.put("longitude", longitude);
mLocations.put("speed", speed);
mLocations.put("time", time);
reference.setValue(mLocations);
Log.e(TAG, "Exiting The run ()");
}
}
To get more information about why the database writes are not completing after 3 minutes, add a CompetionListener to your setValue():
reference.setValue(mLocations, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError == null) {
Log.i(TAG, "onComplete: OKAY");
} else {
Log.e(TAG, "onComplete: FAILED " + databaseError.getMessage());
}
}
});
When you hit the 3 minute mark, if the callback fires with an error, such as permission failure, you can investigate why. If it stops firing at all, that probably means you've lost connection with the Firebase server. You can monitor the connection status using a listener, as described in the documentation.
Related
Geofences are not being triggered using Broadcast Receiver. It was working with IntentService, however it wasn't working when the application is background.. I have changed to Broadcast receiver to make it work irrespective if the application is running in Foreground or background. After I changed it to Broadcast receiver by looking the below example, it stopped working even for Entry and Dwell...
https://github.com/googlesamples/android-play-location/tree/master/Geofencing/app/src/main/java/com/google/android/gms/location/sample/geofencing
Main Activity
public class MainActivity extends AppCompatActivity implements
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status> {
private static final String TAG = "Geofence example";
private static final int MULTIPLE_PERMISSIONS = 1;
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
private static final String ADDRESS_REQUESTED_KEY = "address-request-pending";
private static final String LOCATION_ADDRESS_KEY = "location-address";
String[] permissions = new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.READ_SMS,
Manifest.permission.RECEIVE_SMS,
Manifest.permission.SEND_SMS};
private GeofencingClient mGeofencingClient;
private PendingIntent mGeofencePendingIntent;
private Context mContext;
/**
* Provides access to the Fused Location Provider API.
*/
private FusedLocationProviderClient mFusedLocationClient;
/**
* Represents a geographical location.
*/
private Location mLastLocation;
/**
* Tracks whether the user has requested an address. Becomes true when the user requests an
* address and false when the address (or an error message) is delivered.
*/
private boolean mAddressRequested;
/**
* The formatted location address.
*/
private String mAddressOutput;
/**
* Receiver registered with this activity to get the response from FetchAddressIntentService.
*/
private AddressResultReceiver mResultReceiver;
/**
* Displays the location address.
*/
private TextView mLocationAddressTextView;
/**
* Visible while the address is being fetched.
*/
private ProgressBar mProgressBar;
/**
* Kicks off the request to fetch an address when pressed.
*/
private Button mFetchAddressButton;
private List<Geofence> mGeofenceList;
private GoogleApiClient mGoogleApiClient;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mResultReceiver = new AddressResultReceiver(new Handler());
mLocationAddressTextView = (TextView) findViewById(R.id.location_address_view);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mFetchAddressButton = (Button) findViewById(R.id.fetch_address_button);
// Set defaults, then update using values stored in the Bundle.
mAddressRequested = false;
mAddressOutput = "";
mContext = this;
updateValuesFromBundle(savedInstanceState);
mGeofencingClient = LocationServices.getGeofencingClient(this);
//LatLng latLng = getLocationFromAddress(mContext, "517 Carrville Rd, Richmond Hill, ON L4C 6E5");
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
SmsReceiver.bindListener(new SMSListener() {
#Override
public void messageReceived(String messageText) {
//From the received text string you may do string operations to get the required OTP
//It depends on your SMS format
Log.d("Message", messageText);
Toast.makeText(MainActivity.this, "Message: " + messageText, Toast.LENGTH_LONG).show();
//startIntentService();
}
});
Log.d(GeofenceTransitionsIntentService.TAG, "OnCreate - Building google API client");
buildGoogleApiClient();
Log.d(GeofenceTransitionsIntentService.TAG, "OnCreate - Before permission check");
if (checkPermissions()) {
Log.d(GeofenceTransitionsIntentService.TAG, "OnCreate - inside Permission ");
getAddress();
}
Log.d(GeofenceTransitionsIntentService.TAG, "mGoogleApiClient = " + mGoogleApiClient);
//test_sendNotification("testing");
updateUIWidgets();
}
#Override
public void onStart() {
super.onStart();
Log.d(GeofenceTransitionsIntentService.TAG, "onStart - before Permission ");
if (!mGoogleApiClient.isConnecting() || !mGoogleApiClient.isConnected()) {
mGoogleApiClient.connect();
}
}
#Override
protected void onStop() {
super.onStop();
if (mGoogleApiClient.isConnecting() || mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(this, GeofenceBroadcastReceiver.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
// calling addGeofences() and removeGeofences().
mGeofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.
FLAG_UPDATE_CURRENT);
Log.d(GeofenceTransitionsIntentService.TAG, "getGeofencePendingIntent " + mGeofencePendingIntent);
return mGeofencePendingIntent;
}
/**
* Updates fields based on data stored in the bundle.
*/
private void updateValuesFromBundle(Bundle savedInstanceState) {
if (savedInstanceState != null) {
// Check savedInstanceState to see if the address was previously requested.
if (savedInstanceState.keySet().contains(ADDRESS_REQUESTED_KEY)) {
mAddressRequested = savedInstanceState.getBoolean(ADDRESS_REQUESTED_KEY);
}
// Check savedInstanceState to see if the location address string was previously found
// and stored in the Bundle. If it was found, display the address string in the UI.
if (savedInstanceState.keySet().contains(LOCATION_ADDRESS_KEY)) {
mAddressOutput = savedInstanceState.getString(LOCATION_ADDRESS_KEY);
displayAddressOutput();
}
}
}
/**
* Runs when user clicks the Fetch Address button.
*/
#SuppressWarnings("unused")
public void fetchAddressButtonHandler(View view) {
if (mLastLocation != null) {
Log.d(GeofenceTransitionsIntentService.TAG, "fetchAddressButtonHandler - mLastLocation " + mLastLocation);
startIntentService();
return;
}
// If we have not yet retrieved the user location, we process the user's request by setting
// mAddressRequested to true. As far as the user is concerned, pressing the Fetch Address button
// immediately kicks off the process of getting the address.
mAddressRequested = true;
updateUIWidgets();
}
/**
* Creates an intent, adds location data to it as an extra, and starts the intent service for
* fetching an address.
*/
private void startIntentService() {
// Create an intent for passing to the intent service responsible for fetching the address.
Intent intent = new Intent(this, FetchAddressIntentService.class);
// Pass the result receiver as an extra to the service.
intent.putExtra(Constants.RECEIVER, mResultReceiver);
// Pass the location data as an extra to the service.
intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation);
// Start the service. If the service isn't already running, it is instantiated and started
// (creating a process for it if needed); if it is running then it remains running. The
// service kills itself automatically once all intents are processed.
startService(intent);
Log.d(GeofenceTransitionsIntentService.TAG, "startIntentService - addGeoFenceList " + (mGeofenceList != null));
addGeoFence();
}
/**
* Gets the address for the last known location.
*/
#SuppressWarnings("MissingPermission")
private void getAddress() {
Log.d(GeofenceTransitionsIntentService.TAG, "MainActivity - getAddress () ");
mFusedLocationClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
if (location == null) {
Log.w(GeofenceTransitionsIntentService.TAG, "onSuccess:null");
return;
}
mLastLocation = location;
// Determine whether a Geocoder is available.
if (!Geocoder.isPresent()) {
showSnackbar(getString(R.string.no_geocoder_available));
return;
}
// If the user pressed the fetch address button before we had the location,
// this will be set to true indicating that we should kick off the intent
// service after fetching the location.
if (mAddressRequested) {
startIntentService();
}
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "getLastLocation:onFailure", e);
}
});
}
private GeofencingRequest getGeofencingRequest() {
Log.d(GeofenceTransitionsIntentService.TAG, "MainActivity - getGeofencingRequest ");
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_DWELL | GeofencingRequest.INITIAL_TRIGGER_EXIT);
builder.addGeofences(mGeofenceList);
return builder.build();
}
private void addGeoFence() {
Log.d(GeofenceTransitionsIntentService.TAG, "MainActivity - mGeofenceList () ");
mGeofenceList = new ArrayList<Geofence>();
for (Map.Entry<String, LatLng> entry : Constants.GEO_FENCE_LIST.entrySet()) {
mGeofenceList.add(new Geofence.Builder()
// Set the request ID of the geofence. This is a string to identify this
// geofence.
.setRequestId(entry.getKey())
// Set the circular region of this geofence.
.setCircularRegion(
entry.getValue().latitude,
entry.getValue().longitude,
Constants.GEOFENCE_RADIUS_IN_METERS
)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setLoiteringDelay(6000)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
Geofence.GEOFENCE_TRANSITION_EXIT | Geofence.GEOFENCE_TRANSITION_DWELL)
// Create the geofence.
.build());
}
Log.d(GeofenceTransitionsIntentService.TAG, "addGeoFenceList - mGeofenceList " + mGeofenceList);
//getGeofencingRequest();
if (!mGoogleApiClient.isConnected()) {
mGoogleApiClient.connect();
Toast.makeText(this, "not connected", Toast.LENGTH_SHORT).show();
return;
}
Log.d(GeofenceTransitionsIntentService.TAG, "addGeoFenceList - mGoogleApiClient " + mGoogleApiClient.isConnected());
try {
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
// The GeofenceRequest object.
getGeofencingRequest(),
// A pending intent that that is reused when calling removeGeofences(). This
// pending intent is used to generate an intent when a matched geofence
// transition is observed.
getGeofencePendingIntent()
).setResultCallback(this); // Result processed in onResult().
} catch (SecurityException securityException) {
// Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
Log.d(GeofenceTransitionsIntentService.TAG, "addGeoFenceList - error " + securityException.getLocalizedMessage());
}
}
/**
* Updates the address in the UI.
*/
private void displayAddressOutput() {
mLocationAddressTextView.setText(mAddressOutput);
}
/**
* Toggles the visibility of the progress bar. Enables or disables the Fetch Address button.
*/
private void updateUIWidgets() {
if (mAddressRequested) {
mProgressBar.setVisibility(ProgressBar.VISIBLE);
mFetchAddressButton.setEnabled(false);
} else {
mProgressBar.setVisibility(ProgressBar.GONE);
mFetchAddressButton.setEnabled(true);
}
}
/**
* Shows a toast with the given text.
*/
private void showToast(String text) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save whether the address has been requested.
savedInstanceState.putBoolean(ADDRESS_REQUESTED_KEY, mAddressRequested);
// Save the address string.
savedInstanceState.putString(LOCATION_ADDRESS_KEY, mAddressOutput);
super.onSaveInstanceState(savedInstanceState);
}
#Override
public void onConnected(#Nullable Bundle bundle) {
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
Toast.makeText(
this,
"Geofences Added",
Toast.LENGTH_SHORT
).show();
} else {
// Get the status code for the error and log it using a user-friendly message.
Toast.makeText(
this,
"Geofences error - onResult",
Toast.LENGTH_SHORT
).show();
}
}
/**
* Shows a {#link Snackbar} using {#code text}.
*
* #param text The Snackbar text.
*/
private void showSnackbar(final String text) {
View container = findViewById(android.R.id.content);
if (container != null) {
Snackbar.make(container, text, Snackbar.LENGTH_LONG).show();
}
}
private void showSnackbar(final int mainTextStringId, final int actionStringId,
View.OnClickListener listener) {
Snackbar.make(findViewById(android.R.id.content),
getString(mainTextStringId),
Snackbar.LENGTH_INDEFINITE)
.setAction(getString(actionStringId), listener).show();
}
/**
* Return the current state of the permissions needed.
*/
private boolean checkPermissions() {
int result;
List<String> listPermissionsNeeded = new ArrayList<>();
for (String p : permissions) {
result = ContextCompat.checkSelfPermission(getApplicationContext(), p);
if (result != PackageManager.PERMISSION_GRANTED) {
listPermissionsNeeded.add(p);
}
}
if (!listPermissionsNeeded.isEmpty()) {
ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]), MULTIPLE_PERMISSIONS);
return false;
}
return true;
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MULTIPLE_PERMISSIONS: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permissions granted.
getAddress();
} else {
String perStr = "";
for (String per : permissions) {
perStr += "\n" + per;
}
// permissions list of don't granted permission
}
return;
}
}
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
if (!mGoogleApiClient.isConnecting() || !mGoogleApiClient.isConnected()) {
mGoogleApiClient.connect();
}
Log.d(GeofenceTransitionsIntentService.TAG, "buildGoogleApiClient - mGoogleApiClient " + mGoogleApiClient);
}
private class AddressResultReceiver extends ResultReceiver {
AddressResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// Display the address string or an error message sent from the intent service.
mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY);
displayAddressOutput();
// Show a toast message if an address was found.
if (resultCode == Constants.SUCCESS_RESULT) {
showToast(getString(R.string.address_found));
}
Log.d(GeofenceTransitionsIntentService.TAG, "onReceiveResult - onReceiveResult ");
// Reset. Enable the Fetch Address button and stop showing the progress bar.
mAddressRequested = false;
updateUIWidgets();
}
}
}
JobIntentService:
public class GeofenceTransitionsIntentService extends JobIntentService {
protected static final String TAG = "geofence-transitions";
private static final int JOB_ID = 573;
#Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "GeofenceTransitionsIntentService onCreate ");
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
Log.e(TAG, "GeofenceTransitionsIntentService intent " + intent);
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
String errorMessage = "error";
Log.e(TAG, errorMessage);
return;
}
// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
String geofenceTransitionString = getTransitionString(geofenceTransition);
Log.e(TAG, "geofence_transition_geofenceTransition " + geofenceTransition);
// Test that the reported transition was of interest.
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL || geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
// Get the geofences that were triggered. A single event can trigger multiple geofences.
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
// Get the transition details as a String.
String geofenceTransitionDetails = getGeofenceTransitionDetails(
this,
geofenceTransition,
triggeringGeofences
);
// Send notification and log the transition details.
sendNotification(geofenceTransitionDetails, geofenceTransitionString);
Log.i(TAG, geofenceTransitionDetails);
} else {
// Log the error.
Log.e(TAG, "geofence_transition_invalid_type" + geofenceTransition);
}
}
/**
* Convenience method for enqueuing work in to this service.
*/
public static void enqueueWork(Context context, Intent intent) {
enqueueWork(context, GeofenceTransitionsIntentService.class, JOB_ID, intent);
}
private String getGeofenceTransitionDetails(
Context context,
int geofenceTransition,
List<Geofence> triggeringGeofences) {
String geofenceTransitionString = getTransitionString(geofenceTransition);
// Get the Ids of each geofence that was triggered.
ArrayList triggeringGeofencesIdsList = new ArrayList();
for (Geofence geofence : triggeringGeofences) {
triggeringGeofencesIdsList.add(geofence.getRequestId());
}
String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList);
return geofenceTransitionString + ": " + triggeringGeofencesIdsString;
}
private String getTransitionString(int transitionType) {
switch (transitionType) {
case Geofence.GEOFENCE_TRANSITION_ENTER:
return "Entered";
case Geofence.GEOFENCE_TRANSITION_EXIT:
return "Exited";
case Geofence.GEOFENCE_TRANSITION_DWELL:
return "Dwell";
default:
return "unkwn";
}
}
private void sendNotification(String notificationDetails, String geofenceTransitionString) {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(getApplicationContext(), "notify_geofence");
Intent ii = new Intent(getApplicationContext(), MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, ii, 0);
//Assign BigText style notification
NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
bigText.bigText(notificationDetails);
bigText.setSummaryText(geofenceTransitionString);
mBuilder.setContentIntent(pendingIntent);
mBuilder.setSmallIcon(R.mipmap.ic_launcher_round);
mBuilder.setContentTitle("Geofence notification");
mBuilder.setContentText(notificationDetails);
mBuilder.setPriority(Notification.PRIORITY_MAX);
mBuilder.setStyle(bigText);
NotificationManager mNotificationManager =
(NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("notify_001",
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
}
mNotificationManager.notify(new Random().nextInt(61) + 20, mBuilder.build());
}
}
Broadcast receiver:
public class GeofenceBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Enqueues a JobIntentService passing the context and intent as parameters
GeofenceTransitionsIntentService.enqueueWork(context, intent);
}
}
Logs:
2018-10-30 12:40:02.850 24811-24811/com.lakshmiapps.findme D/geofence-transitions: fetchAddressButtonHandler - mLastLocation Location[fused 43.849304,-79.449305 hAcc=14 et=+2d21h3m43s23ms alt=161.3000030517578 vAcc=2 sAcc=??? bAcc=??? {Bundle[mParcelledData.dataSize=52]}]
2018-10-30 12:40:02.857 24811-24811/com.lakshmiapps.findme D/geofence-transitions: startIntentService - addGeoFenceList false
2018-10-30 12:40:02.857 24811-24811/com.lakshmiapps.findme D/geofence-transitions: MainActivity - mGeofenceList ()
2018-10-30 12:40:07.819 24811-24811/com.lakshmiapps.findme D/geofence-transitions: addGeoFenceList - mGeofenceList [Geofence[CIRCLE id:Ross Doan transitions:7 43.857670, -79.443370 1000m, resp=0s, dwell=6000ms, #-1], Geofence[CIRCLE id:Niagara falls transitions:7 43.078350, -79.081910 1000m, resp=0s, dwell=6000ms, #-1], Geofence[CIRCLE id:Taste of Malayalees transitions:7 43.635190, -79.622830 1000m, resp=0s, dwell=6000ms, #-1], Geofence[CIRCLE id:Home transitions:7 43.849301, -79.449306 1000m, resp=0s, dwell=6000ms, #-1], Geofence[CIRCLE id:Luv2Play transitions:7 43.855660, -79.430200 1000m, resp=0s, dwell=6000ms, #-1]]
2018-10-30 12:40:09.534 24811-24811/com.lakshmiapps.findme D/geofence-transitions: addGeoFenceList - mGoogleApiClient true
2018-10-30 12:40:09.534 24811-24811/com.lakshmiapps.findme D/geofence-transitions: MainActivity - getGeofencingRequest
2018-10-30 12:40:13.598 24811-24811/com.lakshmiapps.findme D/geofence-transitions: getGeofencePendingIntent PendingIntent{5fddab6: android.os.BinderProxy#47dc3b7}
2018-10-30 12:40:13.631 4938-5579/? I/GeofencerStateMachine: addGeofences called by com.lakshmiapps.findme
2018-10-30 12:40:13.631 4726-4773/? E/GmsLocationManagerService_FLP: [GeofencingApi] AddGeofence from com.lakshmiapps.findme and size = 5
2018-10-30 12:40:13.632 4726-4773/? D/GeofenceRequestInfo_FLP: removeExpiredGeofences - com.lakshmiapps.findme: 0 fences
2018-10-30 12:40:13.632 4726-4773/? E/GeofenceRequestManager_FLP: [GeofencingApi] AddGeofence 5 fences from com.lakshmiapps.findme
2018-10-30 12:40:13.632 4726-4773/? D/GeofenceRequestManager_FLP: 1540668973552 gpslastknowntime: 243443714 / nlplastknowntime: 248290794
2018-10-30 12:40:16.290 24811-24811/com.lakshmiapps.findme D/geofence-transitions: onReceiveResult - onReceiveResult
2018-10-30 12:44:08.692 4938-24505/? I/GeofencerStateMachine: removeGeofences: removeRequest=RemoveGeofencingRequest[REMOVE_BY_PENDING_INTENT pendingIntent=PendingIntent[creatorPackage=com.marriott.mrt], packageName=null]
Thanks,
Ramesh
Adding
<uses-permission android:name="android.permission.WAKE_LOCK" />
to AndroidManifest.xml seems to solve this issue in my case
I got some problems to keep a location tracking for my Android app.
The service we developed are all working, but depending on the phones and the API level, they are killed soon or later.
My purpose is to notify a user when he is getting out of a certain range, and this is working when the app is running.
This is the service that calculates when the user is in or out of the zone:
public class GeoFenceService extends BaseService {
#Inject
GeoFenceRepository geoFenceRepository;
SettingValueService settingValueService = new SettingValueService();
GeoFence geoFence;
private GeofencingClient mGeofencingClient;
PendingIntent mGeofencePendingIntent;
List<Geofence> mGeofenceList = new ArrayList<>();
public GeoFenceService() {
super();
Injector.getInstance().getAppComponent().inject(this);
}
public Observable<GeoFence> saveGeofence(GeoFence geoFence) {
if (geoFence.getId() == null)
geoFence.setId(CommonMethods.generateUuid());
commitToRealm(geoFence);
if (Application.getHasNetwork())
return geoFenceRepository.saveGeofence(geoFence);
else
return Observable.just(null);
}
public Observable<GeoFence> getGeofence() {
return geoFenceRepository.getGeofence();
}
public GeoFence getGeofenceFromRealm() {
Realm realm = Realm.getDefaultInstance();
RealmQuery<GeoFence> query = realm.where(GeoFence.class);
GeoFence result = query.findFirst();
if (result != null)
return realm.copyFromRealm(result);
return null;
}
public void initGeoFence(Context context) {
SettingValue autoCloking = settingValueService.getSettingByName("auto_clocking");
if (autoCloking != null && autoCloking.getValue().equals("true")) {
if (mGeofencingClient == null)
mGeofencingClient = LocationServices.getGeofencingClient(context);
geoFence = getGeofenceFromRealm();
if (geoFence != null) {
addGeofence(geoFence, context);
}
}
}
#SuppressLint("MissingPermission")
public void addGeofence(GeoFence geofence, Context context) {
mGeofenceList.clear();//only 1 geofence at the same time.
mGeofenceList.add(new Geofence.Builder()
// Set the request ID of the geofence. This is a string to identify this
// geofence.
.setRequestId(geofence.getId())
.setCircularRegion(
geofence.getLatitude(),
geofence.getLongitude(),
(float) geofence.getRadius()
)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setLoiteringDelay(30000)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
Geofence.GEOFENCE_TRANSITION_EXIT | Geofence.GEOFENCE_TRANSITION_DWELL)
.build());
mGeofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent(context));
}
private PendingIntent getGeofencePendingIntent(Context context) {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(context, GeofencingService.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
// calling addGeofences() and removeGeofences().
mGeofencePendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.
FLAG_UPDATE_CURRENT);
return mGeofencePendingIntent;
}
private GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_EXIT);
builder.addGeofences(mGeofenceList);
return builder.build();
}
public void removeGeofence(Context context) {
if (mGeofencingClient != null)
mGeofencingClient.removeGeofences(getGeofencePendingIntent(context));
}
}
Even if this was working in the background we made a service to wake up the GPS of the phone every 60 sec following the advice of this question.
This is the service we made:
public class UpdateLocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
private static final String TAG = "UpdateLocationService";
private static final int LOCATION_INTERVAL = 60000;
private Context mContext;
private LocationRequest locationRequest;
private GoogleApiClient googleApiClient;
private FusedLocationProviderApi fusedLocationProviderApi;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onCreate() {
Log.e(TAG, "onCreate");
mContext = this;
getLocation();
}
#Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
super.onDestroy();
try {
if (googleApiClient != null) {
googleApiClient.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void getLocation() {
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(LOCATION_INTERVAL);
locationRequest.setFastestInterval(LOCATION_INTERVAL);
fusedLocationProviderApi = LocationServices.FusedLocationApi;
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
if (googleApiClient != null) {
googleApiClient.connect();
}
}
#Override
public void onConnected(Bundle arg0) {
// Location location =
fusedLocationProviderApi.getLastLocation(googleApiClient);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
fusedLocationProviderApi.requestLocationUpdates(googleApiClient, locationRequest, this);
}
#Override
public void onConnectionSuspended(int arg0) {
}
#Override
public void onLocationChanged(Location location) {
Toast.makeText(mContext, "User location :"+location.getLatitude()+" , "+location.getLongitude(), Toast.LENGTH_SHORT).show();
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
}
}
and finally this is the call in our main activity:
if (checkGpsProviderEnabled()) {
geoFenceService.initGeoFence(this);
Intent msgIntent = new Intent(this, GeofencingService.class);
startService(msgIntent);
startService(new Intent(this,UpdateLocationService.class));
}
Of course, we implement the authorization for Localisation and service déclaration in our AndroidManifest
And everything is working fine when the app isn't in the foreground
The foreground is working on some device but not all, and I wanted to know if I can do something more to force our services to run all the time in the background
Thanks in advance
I need to get continuous location updates from the application for my application. To do so, I followed the Android Developer Guide to get location updates. Because location updates can't be received in background in API 26, I added a foreground service (Background location limits). However, I still only receive updates when an other activity which requests location updates is in the foreground.
Location Service:
public class LocationUpdateService extends Service {
private static final String TAG = LocationUpdateService.class.getSimpleName();
private static final String NOTIFICATION_CHANNEL_ID = "TrackNotification";
private static final int FOREGROUND_SERVICE_ID = 1;
private static final int NOTIFICATION_ID = 1;
public static final String STATUS_INTENT = "status";
private static final int CONFIG_CACHE_EXPIRY = 600;
private NotificationManager mNotificationManager;
private NotificationCompat.Builder mNotificationBuilder;
private DatabaseReference mDatabaseReference;
private FusedLocationProviderClient mFusedLocationProviderClient;
private LocationRequest mLocationRequest;
private FirebaseRemoteConfig mFirebaseRemoteConfig;
private String uid;
#Override
public void onCreate() {
super.onCreate();
uid = FirebaseAuth.getInstance().getUid();
if(uid == null)
stopSelf();
mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder().build();
mFirebaseRemoteConfig.setConfigSettings(configSettings);
mFirebaseRemoteConfig.setDefaults(R.xml.remode_config_defaults);
fetchRemoteConfig();
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
mDatabaseReference = FirebaseDatabase.getInstance().getReference();
mLocationRequest = new LocationRequest()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setFastestInterval(mFirebaseRemoteConfig.getLong("LOCATION_REQUEST_INTERVAL"))
.setFastestInterval(mFirebaseRemoteConfig.getLong("LOCATION_REQUEST_INTERVAL_FASTEST"));
bindNotification();
setStatusMessage(R.string.connecting);
startLocationTracking();
}
private LocationCallback mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
Log.d(TAG,"Got location update!");
if(locationResult == null)
return;
for(Location location : locationResult.getLocations()) {
CustomLocation customLocation = LocationAdapter.toDatabaseLocation(location);
mDatabaseReference.child("locations").child(uid).setValue(customLocation);
}
}
#Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
locationAvailability.isLocationAvailable();
// TODO handle no location here
super.onLocationAvailability(locationAvailability);
}
};
#SuppressWarnings({"MissingPermission"})
private void startLocationTracking() {
mFusedLocationProviderClient.requestLocationUpdates(mLocationRequest,mLocationCallback, Looper.myLooper());
}
private void fetchRemoteConfig() {
mFirebaseRemoteConfig.fetch(CONFIG_CACHE_EXPIRY)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.i(TAG,"Remote config fetched");
mFirebaseRemoteConfig.activateFetched();
}
});
}
#Override
public void onDestroy() {
setStatusMessage(R.string.not_tracking);
mNotificationManager.cancel(NOTIFICATION_ID);
mFusedLocationProviderClient.removeLocationUpdates(mLocationCallback);
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) { return null; }
private void bindNotification() {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this,0,
new Intent(this, MainActivity.class),PendingIntent.FLAG_UPDATE_CURRENT);
mNotificationBuilder = new NotificationCompat.Builder(this,NOTIFICATION_CHANNEL_ID)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setShowWhen(false)
.setSmallIcon(R.drawable.ic_car)
// .setColor(getColor(R.color.colorPrimary))
.setContentTitle(getString(R.string.app_name))
.setOngoing(true)
.setContentIntent(resultPendingIntent);
startForeground(FOREGROUND_SERVICE_ID, mNotificationBuilder.build());
}
/**
*
* #param message Status message to display
*/
private void setStatusMessage(String message) {
mNotificationBuilder.setContentText(message);
mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
Intent intent = new Intent(STATUS_INTENT);
intent.putExtra(getString(R.string.status),message);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
private void setStatusMessage(int resID) {
setStatusMessage(getString(resID));
}
}
And I gets started with
startService(new Intent(this,LocationUpdateService.class));
Android Manifest:
<service android:name=".LocationUpdateService" />
EDIT1: I now tested it on older API versions (22) and the problem is still the same: As long as some app with location requests is in the foreground it works, otherwise not.
Maybe it's a problem with the FusedLocationProviderClient but I don't know what. I only found code examples with the old FusedLocationProvider API which is now deprecated.
Have you tried debugging to make sure your service is being hit?
This might sound silly, but have you checked if your service is registered in your manifest? I know I've definitely run into that issue.
<service android:name=".LocationService"
android:label="Location Service"
android:exported="true"
android:enabled="true"
android:process=":location_background_service"/>
For getting the location, when I set mine up, I created a class that implemented android.location.LocationListener.
private class LocationListener implements android.location.LocationListener {
Location mLastLocation;
public LocationListener(String provider) {
Log.e(TAG, "LocationListener " + provider);
mLastLocation = new Location(provider);
}
#Override
public void onLocationChanged(Location location) {
Log.e(TAG, "onLocationChanged: " + location);
mLastLocation.set(location);
}
#Override
public void onProviderDisabled(String provider) {
Log.e(TAG, "onProviderDisabled: " + provider);
}
#Override
public void onProviderEnabled(String provider) {
Log.e(TAG, "onProviderEnabled: " + provider);
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.e(TAG, "onStatusChanged: " + provider);
}
}
You can create multiple instances for different providers. In my case, I ended up using 2.
LocationListener[] mLocationListeners = new LocationListener[]{
new LocationListener(LocationManager.GPS_PROVIDER),
new LocationListener(LocationManager.NETWORK_PROVIDER)
};
Then I initialized a LocationManager that can set a polling rate for each a LocationListener.
private void initializeLocationManager() {
Log.e(TAG, "initializeLocationManager");
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
Then in your Service onCreate function, initialize your LocationManager, and use one of the listeners as your main source and the other as a fallback.
try {
mLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
5 * 60 * 1000, //5 Minutes
1F /*METERS*/,
mLocationListeners[0]
);
} catch (java.lang.SecurityException ex) {
Log.e(TAG, "failed to request location update. Insufficient permissions. ", ex);
try {
mLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
5 * 60 * 1000, //5 Minutes
1F /*METERS*/,
mLocationListeners[1]
);
} catch (java.lang.SecurityException e) {
Log.e(TAG, "failed to request location update. Insufficient permissions. ", e);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Network provider does not exist.", e);
}
} catch (IllegalArgumentException ex) {
Log.e(TAG, "Network provider does not exist.", ex);
}
}
(Sorry if this code is gross, it's a quick and dirty example.)
I am trying to implement a background GPS location service in android using Service and LocationListener. The service is started (-> the onCreate and onStartCommand methods are called), but the onLocationChanged method never called.
Here is the code of my Service:
public class GpsHandler extends Service implements LocationListener{
private static final String TAG = "GpsHandler";
private LocationManager mLocationManager = null;
private static final int LOCATION_INTERVAL = 1000;
private static final float LOCATION_DISTANCE = 10f;
private final String LOCATION_BROADCAST_TAG = "android.LOCATION";
private final String LOCATION_EXTRA_TAG = "Location";
private Location mLastLocation = null;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate()
{
Toast.makeText(this, "Service Started, onCreate", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Let it continue running until it is stopped.
Toast.makeText(this, "Service Started, onStartCommand", Toast.LENGTH_LONG).show();
if (mLocationManager == null)
{
mLocationManager = (LocationManager)getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
mLastLocation = new Location(LocationManager.GPS_PROVIDER);
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE, this);
} catch (java.lang.SecurityException ex) {
Log.i(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.d(TAG, "gps provider does not exist " + ex.getMessage());
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if (mLocationManager != null)
{
try {
mLocationManager.removeUpdates(this);
} catch (Exception ex) {
Log.i(TAG, "fail to remove location listners, ignore", ex);
}
}
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
}
#Override
public void onProviderDisabled(String provider) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
Toast.makeText(getBaseContext(), "Gps is turned off!! ",
Toast.LENGTH_SHORT).show();
}
#Override
public void onLocationChanged(Location location)
{
Toast.makeText(this, "Location Changed", Toast.LENGTH_LONG).show();
if(isBetterLocation(location,mLastLocation))
{
mLastLocation.set(location);
Intent intent = new Intent(LOCATION_BROADCAST_TAG).putExtra(LOCATION_EXTRA_TAG, location);
sendBroadcast(intent);
}
}
#Override
public void onProviderEnabled(String provider){}
#Override
public void onStatusChanged(String provider, int status, Bundle extras){}
private boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > 1000*30;//30000ms = 30 sec
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer)
{
return true;
// If the new location is more than two minutes older, it must be worse
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
}
Finally I have found the solution. I have tried to debug it with an other emulator (Originally I have used Pixel C emulator) and now it works flawless. So it is just a Studio Bug, the code is working.I have also restructured the code a bit, removing some useless functions:
Working code:
public class GpsHandler extends Service implements LocationListener{
private static final String TAG = "GpsHandler";
private LocationManager mLocationManager = null;
private static final int LOCATION_INTERVAL = 0;
private static final float LOCATION_DISTANCE = 0f;
private final String LOCATION_BROADCAST_TAG = "android.LOCATION";
private final String LOCATION_EXTRA_TAG = "Location";
private Location mLastLocation = null;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate()
{
Toast.makeText(this, "Service Started, onCreate", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Let it continue running until it is stopped.
Toast.makeText(this, "Service Started, onStartCommand", Toast.LENGTH_LONG).show();
if (mLocationManager == null)
{
mLocationManager = (LocationManager)getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
mLastLocation = new Location(LocationManager.GPS_PROVIDER);
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE, this);
mLastLocation.set(mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER));
} catch (java.lang.SecurityException ex) {
Log.i(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.d(TAG, "gps provider does not exist " + ex.getMessage());
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if (mLocationManager != null)
{
try {
mLocationManager.removeUpdates(this);
} catch (Exception ex) {
Log.i(TAG, "fail to remove location listners, ignore", ex);
}
}
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
}
#Override
public void onProviderDisabled(String provider) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
Toast.makeText(getBaseContext(), "Gps is turned off!! ",
Toast.LENGTH_SHORT).show();
}
#Override
public void onLocationChanged(Location location)
{
Toast.makeText(this, "Location Changed", Toast.LENGTH_LONG).show();
if(location.getAccuracy()<4*mLastLocation.getAccuracy())
{
mLastLocation.set(location);
Intent intent = new Intent(LOCATION_BROADCAST_TAG).putExtra(LOCATION_EXTRA_TAG, location);
sendBroadcast(intent);
}
}
#Override
public void onProviderEnabled(String provider){}
#Override
public void onStatusChanged(String provider, int status, Bundle extras){}
}
Business scenario:
The objective of business scenario is to update our longitude and latitude to server for an interval of every 15 mins in background
I have used Alaram Manger (Restarting Service when service gets shut down by android os)
i have shared my code below :
public class GoogleClientFusedApiService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
private static final String TAG = "LocationService";
private boolean currentlyProcessingLocation = false;
private LocationRequest locationRequest;
private GoogleApiClient googleApiClient;
public static Boolean isRunning = false;
// The minimum time between updates in milliseconds
private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 15 minutes
#Override
public void onCreate() {
super.onCreate();
}
Handler mHandler = new Handler();
Runnable mHandlerTask = new Runnable() {
#Override
public void run() {
if (!isRunning) {
startTracking();
}
mHandler.postDelayed(mHandlerTask, 1000 * 60 * 2);
}
};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// if we are currently trying to get a location and the alarm manager has called this again,
// no need to start processing a new location.
if (!currentlyProcessingLocation) {
currentlyProcessingLocation = true;
//startTracking();
mHandlerTask.run();
}
//alarm.setAlarm(this);
return START_STICKY;
// return START_NOT_STICKY;
}
private void startTracking() {
Log.d(TAG, "startTracking");
if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS) {
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
if (!googleApiClient.isConnected() || !googleApiClient.isConnecting()) {
googleApiClient.connect();
}
} else {
Log.e(TAG, "unable to connect to google play services.");
}
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onLocationChanged(Location location) {
if (location != null) {
String latitude = String.valueOf(location.getLatitude());
String longtitude = String.valueOf(location.getLongitude());
LocationUpdateServerCall locationUpdateServerCall = new LocationUpdateServerCall(getApplicationContext());
locationUpdateServerCall.pushLocationUpdateServerCall(latitude, longtitude);
Log.e(TAG, "position: " + location.getLatitude() + ", " + location.getLongitude() + " accuracy: " + location.getAccuracy());
// we have our desired accuracy of 500 meters so lets quit this service,
// onDestroy will be called and stop our location uodates
/* if (location.getAccuracy() < 10.0f) {
stopLocationUpdates();
}*/
}
}
private void stopLocationUpdates() {
if (googleApiClient != null && googleApiClient.isConnected()) {
googleApiClient.disconnect();
}
mHandler.removeCallbacks(mHandlerTask);
}
/**
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
#Override
public void onConnected(Bundle bundle) {
Log.d(TAG, "onConnected");
locationRequest = LocationRequest.create();
locationRequest.setInterval(MIN_TIME_BW_UPDATES); // milliseconds
locationRequest.setFastestInterval(MIN_TIME_BW_UPDATES); // the fastest rate in milliseconds at which your app can handle location updates
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setSmallestDisplacement(10); // distance travelled
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
if (googleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(
googleApiClient, locationRequest, this);
} else {
return;
}
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.e(TAG, "onConnectionFailed");
startTracking();
/* stopLocationUpdates();
stopSelf();*/
}
#Override
public void onConnectionSuspended(int i) {
Log.e(TAG, "GoogleApiClient connection has been suspend");
startTracking();
}
//Start Alarm(Start Location Updates on Background)
#Override
public void onTaskRemoved(Intent rootIntent) {
Intent restartServiceTask = new Intent(getApplicationContext(), this.getClass());
restartServiceTask.setPackage(getPackageName());
PendingIntent restartPendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceTask, PendingIntent.FLAG_ONE_SHOT);
AlarmManager myAlarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
myAlarmService.setRepeating(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime(), 1000 * 60 * 1
, restartPendingIntent);
super.onTaskRemoved(rootIntent);
}
// Canacel Alarm (Stop Location Update)
public void cancelAlarm(Activity context) {
Intent intent = new Intent(context, GoogleClientFusedApiService.class);
PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(sender);
}
Advance Thanks
Waiting For Valuable Guidelines