I have created a sample foreground service to check if it's possible to fetch location from background after every 15 mins or 30 mins. With introduction to doze mode alarm manager does not work at exact time.
In foreground service I have tried using handler post delay but that also does not work as I am getting intervals of upto 3 hrs. In the below code I have used ScheduledThreadPoolExecutor and it performed even worse. I got delay of upto 12 hours. I have tested this on android 6.0 as of now.
Can anyone suggest a work around for this?
package com.hitec16.foregroundservicetest;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.FusedLocationProviderApi;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import java.text.DateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyService extends Service
implements LocationListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = "MyService";
private static final long INTERVAL = 1000 * 10;
private static final long FASTEST_INTERVAL = 1000 * 5;
private static final long WAIT_INTERVAL = 1000 * 30;
private ScheduledThreadPoolExecutor mExecutor;
private int mCounter = 0;
private FusedLocationProviderApi fusedLocationProviderApi = LocationServices.FusedLocationApi;
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
Location mCurrentLocation;
private String mLastUpdateTime;
public MyService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Looper.prepare();
onStart_(intent, flags, startId);
Looper.loop();
}
});
thread.start();
// onStart_(intent,flags,startId);
return START_STICKY;
}
private void onStart_(Intent intent, int flags, int startId) {
if (intent.getAction().equals(Constants.STARTFOREGROUND_ACTION)) {
Logger.d("Received Start Foreground Intent ");
createLocationRequest();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
connectToGoogleApi();
showNotification();
Toast.makeText(getApplicationContext(), "Service Started!", Toast.LENGTH_SHORT).show();
Logger.d("work finished");
// fetchLocationAgain();
mExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(2);
mExecutor.scheduleWithFixedDelay(new Runnable() {
#Override
public void run() {
try {
startLocationUpdates();
} catch (Exception e) {
Log.d(getClass().getSimpleName(), "Exception caught: " + e.getMessage());
}
Log.d("Test", "test");
}
}, 30, 60 * 15, TimeUnit.SECONDS);
}
}
private void showNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Constants.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Fused Location Service")
.setTicker("ticker text")
.setContentText("My Location")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, MyService.this);
mCounter = 0;
Logger.d("Location update stopped .......................");
}
#Override
public void onConnected(#Nullable Bundle bundle) {
Logger.d("onConnected - isConnected ...............: " + mGoogleApiClient.isConnected());
try {
startLocationUpdates();
} catch (Exception e) {
Log.d(getClass().getSimpleName(), "Exception caught: " + e.getMessage());
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Logger.d("Connection failed: " + connectionResult.toString());
}
#Override
public void onLocationChanged(Location location) {
if (mCounter < 3) {
Log.d(getClass().getSimpleName(), "returned....");
mCounter++;
return;
}
Logger.d("Firing onLocationChanged..............................................");
mCurrentLocation = location;
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
Logger.d("Location latitude and longitude :" + location.getLatitude() + " ," + location.getLongitude()
+ " , Accuracy : " + location.getAccuracy() + ", location provider : " + location.getProvider() + "Time : " + mLastUpdateTime);
stopLocationUpdates();
}
protected void startLocationUpdates() throws Exception {
PendingResult<Status> pendingResult = LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this, Looper.getMainLooper());
Logger.d("Location update started ..............: ");
}
#Override
public void onDestroy() {
disconnectFromGoogleApi();
Log.d(getClass().getSimpleName(), "on destroy called");
mExecutor.shutdown();
super.onDestroy();
}
private void connectToGoogleApi() {
Logger.d("connectToGoogleApi fired ..............");
mGoogleApiClient.connect();
}
private void disconnectFromGoogleApi() {
Logger.d("disConnectFromGoogleApi fired ..............");
mGoogleApiClient.disconnect();
}
}
Related
I am trying to get location after every 10 second using FusedLocationProviderClient, However when i minimize the app or terminate it the location updates stops. Below is my code. Right now i am using android 9.0. I have no idea why this is happening any help will be grateful
Android Menifest
<service android:enabled="true" android:name=".LocationService">
</service>
LocationService.java
package com.example.locationupdate;
import android.Manifest;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.example.locationupdate.db.Realm$Helper;
import com.example.locationupdate.shared_pref.SaveInSharedPreference;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.SettingsClient;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import java.util.List;
import static com.google.android.gms.location.LocationServices.getFusedLocationProviderClient;
public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
//private LocationRequest mLocationRequest;
private long UPDATE_INTERVAL = 10 * 1000; /* 10 secs */
private long FASTEST_INTERVAL = 2000;
int count = 0;
List<FilterData> datafromDB;
FusedLocationProviderClient mFusedLocationClient;
LocationRequest mLocationRequest;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
LocationCallback mLocationCallback = new LocationCallback(){
#Override
public void onLocationResult(LocationResult locationResult) {
for (Location location : locationResult.getLocations()) {
//Log.e("MainActivity", "Location: " + location.getLatitude() + " " + location.getLongitude());
if (SaveInSharedPreference.getInSharedPreference(LocationService.this).getLat() == 0.0 && SaveInSharedPreference.getInSharedPreference(LocationService.this).getLng() == 0.0) {
SaveInSharedPreference.getInSharedPreference(LocationService.this).setLatLong(location.getLatitude(), location.getLongitude());
}
SaveInSharedPreference.getInSharedPreference(LocationService.this).setCurrentLatLong(location.getLatitude(), location.getLongitude());
count = count + 1;
Log.e("mLocationCallbackLat", String.valueOf(location.getLatitude()));
Log.e("mLocationCallbackLong", String.valueOf(location.getLongitude()));
//LocationMatch(location);
}
/* datafromDB = Realm$Helper.getLocation$Module(LocationService.this).getAllData();
for (FilterData str : datafromDB) {
String name = str.getName();
}*/
};
};
public void onResume() {
Log.e("OnResume", "OnResumeEvent");
if (mFusedLocationClient != null) {
requestLocationUpdates();
}
}
public void requestLocationUpdates() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL); // two minute interval
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
}
}
public void onPause() {
//super.onPause();
Log.e("ONPause", "OnPauseEvent");
if (mFusedLocationClient != null) {
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
}
}
protected void startLocationUpdates() {
// Create the location request to start receiving updates
mLocationRequest = new LocationRequest();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
// Create LocationSettingsRequest object using location request
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
// Check whether location settings are satisfied
// https://developers.google.com/android/reference/com/google/android/gms/location/SettingsClient
SettingsClient settingsClient = LocationServices.getSettingsClient(this);
settingsClient.checkLocationSettings(locationSettingsRequest);
// new Google API SDK v11 uses getFusedLocationProviderClient(this)
getFusedLocationProviderClient(this).requestLocationUpdates(mLocationRequest, new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
// do work here
onLocationChanged(locationResult.getLastLocation());
}
},
Looper.myLooper());
}
public void onLocationChanged(Location location) {
if (SaveInSharedPreference.getInSharedPreference(LocationService.this).getLat() == 0.0 && SaveInSharedPreference.getInSharedPreference(LocationService.this).getLng() == 0.0) {
SaveInSharedPreference.getInSharedPreference(LocationService.this).setLatLong(location.getLatitude(), location.getLongitude());
}
SaveInSharedPreference.getInSharedPreference(LocationService.this).setCurrentLatLong(location.getLatitude(), location.getLongitude());
Toast.makeText(this,"onLocationChanged",Toast.LENGTH_LONG);
// New location has now been determined
String msg = "Updated Location: " +
Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Log.e ("onLocationChangedLat", String.valueOf(location.getLatitude()));
Log.e ("onLocationChangedLong", String.valueOf(location.getLongitude()));
// You can now create a LatLng Object for use with maps
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
/*loc1.setLatitude(location.getLatitude());
loc1.setLongitude(location.getLongitude());
loc2.setLatitude(str.getLat());
loc2.setLongitude(str.getlng());
double distanceInMeters = loc1.distanceTo(loc2);
Log.e("Name", name);
Log.e("Distance", "" + distanceInMeters);*/
}
public void getLastLocation() {
// Get last known recent location using new Google Play Services SDK (v11+)
FusedLocationProviderClient locationClient = getFusedLocationProviderClient(this);
locationClient.getLastLocation()
.addOnSuccessListener(new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// GPS location can be null if GPS is switched off
if (location != null) {
onLocationChanged(location);
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("MapDemoActivity", "Error trying to get last GPS location");
e.printStackTrace();
}
});
}
#Override
public void onConnected(#Nullable Bundle bundle) {
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
}
MainActitvity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//locationTv = findViewById(R.id.location);
Intent startIntent = new Intent(this, LocationService.class);
startService(startIntent);
You can achieve this by setting up a handler and adding the required permissions to the android app. Apperantly there is a limit to how many times you can request the location in the background according to Android Location In Background but i have not seen this. Im requesting a new location every second.
First add these to your manifest:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Then you can create a handler like this.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
createLocationHandler();
...
}
The createLocationHandler and isServiceRunning function:
public void createLocationHandler(){
HandlerThread locationHandler = new HandlerThread("LocationHandler"); //Creates a new handler thread
locationHandler.start(); //Starts the thread
Handler handler = new Handler(locationHandler.getLooper()); //Get the looper from the handler thread
handler.postDelayed(new Runnable() {//Run the runnable only after the given time
#Override
public void run() {
//Check if the location service is running, if its not. lets start it!
if(!isMyServiceRunning(LocationService.class)){
getApplicationContext().startService(new Intent(getApplicationContext(), LocationService.class));
}
//Requests a new location from the location service(Feel like it could be done in a less static way)
LocationService.requestNewLocation();
createLocationHandler();//Call the create location handler again, this will not be added to the stack because of the looper.
}
}, 10000);//Set the delay to be 10 seconds, 1 second = 1000 milliseconds
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) this.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Remember to ask for the background_location permission before you want to start the service.
I'm having trouble trying to get the latitude and longitude from my Service and store them as variables so I can access them from other classes I'm building. I want to have them equal to the current latitude and longitude of the user and update them through the interval set in the requestLocation() method. I would like to store them as variables as I would like to perform some calculations using them and I have also written a Content Provider/Database Helper in order to store them in my local Sqlite database but can't right now as I cannot access them. Below can be seen my code for my Location service and MainActivity.
Location Service
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
public class LocationService extends Service {
FusedLocationProviderClient fusedLocationProviderClient;
LocationCallback locationCallback;
private final IBinder binder = new LocalBinder();
//public double latitude;
//public double longitude;
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public void onCreate() {
super.onCreate();
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
locationCallback = new LocationCallback(){
// Whenever there is a Location Update, this method is where it occurs
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
// Log Result for Longitude and Latitude, call method to receive elsewhere
Log.d("myLog", "Latitude is: " + locationResult.getLastLocation().getLatitude() + ", " +
"Longitude is: " + locationResult.getLastLocation().getLongitude());
Intent intent = new Intent("ACT_LOC");
intent.putExtra("Latitude", locationResult.getLastLocation().getLatitude());
intent.putExtra("Longitude", locationResult.getLastLocation().getLongitude());
sendBroadcast(intent);
//double latitude = locationResult.getLastLocation().getLatitude();
//double longitude = locationResult.getLastLocation().getLongitude();
}
};
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
requestLocation();
return super.onStartCommand(intent, flags, startId);
}
// Method to request the Location every 3 seconds
private void requestLocation(){
LocationRequest locationRequest = new LocationRequest();
locationRequest.setInterval(3000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
}
public class LocalBinder extends Binder {
LocationService getService(){
return LocationService.this;
}
}
}
MainActivity
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 23) {
// If the permission Access Fine Location is not granted
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// Request permissions again
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
} else {
// Start Location Service
startService();
}
} else {
// Start Location Service
startService();
}
}
// Start the service with a new intent for the MainActivity and Location Services
// Register Broadcast Receiver with intent action from LocationService.java
void startService() {
LocationBroadcastReceiver receiver = new LocationBroadcastReceiver();
IntentFilter filter = new IntentFilter("ACT_LOC");
Intent intent = new Intent(this, LocationService.class);
registerReceiver(receiver, filter);
startService(intent);
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startService();
} else {
Toast.makeText(this, "Permissions Required", Toast.LENGTH_LONG).show();
}
}
}
public class LocationBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// check if action is required or not
if (intent.getAction().equals("ACT_LOC")) {
double lat = intent.getDoubleExtra("Latitude", 0f);
double lng = intent.getDoubleExtra("Longitude", 0f);
Toast.makeText(MainActivity.this, "Latitude is: " + lat + ", Longitude is: " + lng, Toast.LENGTH_LONG).show();
}
}
}
}
i want to make an application that i can run it over voice like siri or google assistant, so in order to make it i have to implement the code in a background or foreground service which will run speech recognizer
so is this possible,because of the limitation of the working services on android Oreo and higher this make me stuck in the middle of nowhere
and i'm not so professional so i could figure this out myself
i have tried to make a foreground service with IntentService so that it could make the work on the background without freezing the UI
and the Speech recognizer didn't work
package com.example.intentservice;
import android.app.IntentService;
import android.app.Notification;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
import java.util.List;
import java.util.Locale;
import static com.example.intentservice.App.CHANNEL_ID;
public class ExampleService extends IntentService {
public static final String TAG = "ExampleService";
private PowerManager.WakeLock wakeLock;
private TextToSpeech textToSpeech;
private SpeechRecognizer recognizer;
#Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate");
initRec();
startListening();
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ExampleService:wakelock");
wakeLock.acquire();
Log.e(TAG, "wakelock acquired");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification notification = new Notification.Builder(this, CHANNEL_ID)
.setContentTitle("noti")
.setContentText("running")
.build();
startForeground(1, notification);
}
}
private void startListening() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS,1);
recognizer.startListening(intent);
}
private void initRec() {
if (SpeechRecognizer.isRecognitionAvailable(this)) {
recognizer = SpeechRecognizer.createSpeechRecognizer(this);
recognizer.setRecognitionListener(new RecognitionListener() {
#Override
public void onReadyForSpeech(Bundle bundle) {
}
#Override
public void onBeginningOfSpeech() {
}
#Override
public void onRmsChanged(float v) {
}
#Override
public void onBufferReceived(byte[] bytes) {
}
#Override
public void onEndOfSpeech() {
}
#Override
public void onError(int i) {
}
#Override
public void onResults(Bundle bundle) {
List<String> res = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
Toast.makeText(ExampleService.this, res.get(0), Toast.LENGTH_SHORT).show();
}
#Override
public void onPartialResults(Bundle bundle) {
}
#Override
public void onEvent(int i, Bundle bundle) {
}
});
}
}
public ExampleService() {
super("ExampleService");
// to create service again
setIntentRedelivery(true);
}
// this just to test the code
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Log.e(TAG, "onHandleIntent");
String string = intent.getStringExtra("key");
for (int i = 0; i < 10; i++) {
Log.e(TAG, string + "-" + i);
// Toast.makeText(this, "i", Toast.LENGTH_SHORT).show();
SystemClock.sleep(1000);
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
wakeLock.release();
Log.e(TAG, "wakelock realised");
}
}
I have been trying to implement foreground service to get location after every 3 sec even when the app is in the background by showing a notification. But when I remove my app from the background notification also removes. However, this is only occurring when I am doing it in MI REDMI NOTE 5(API version 28) and MI REDMI NOTE 4(API version 24) but when I ran the same app in Samsung J5(API version 23), the notification is showing even when the app is removed from background until it is manually stopped from the activity. Is the varied result behavior is due to change in API or is it because of different phone model?
Here's my Service Class
package com.example.locationrunandall;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
public class ForeService extends Service {
private static final String PACKAGE_NAME =
"com.example.customizedforeground";
static final String ACTION_BROADCAST = PACKAGE_NAME + ".broadcast";
static final String EXTRA_LOCATION = PACKAGE_NAME + ".location";
private static final String EXTRA_STARTED_FROM_NOTIFICATION = PACKAGE_NAME +
".started_from_notification";
private Handler mServiceHandler;
private NotificationManager mNotificationManager;
// private Notification notification;
private LocationRequest mLocationRequest;
private FusedLocationProviderClient mFusedLocationClient;
private LocationCallback mLocationCallback;
private Location mLocation;
private static final String CHANNEL_ID = "CHANNEL_ONE";
private static final int NOTIFICATION_ID = 4567123;
private static final String TAG = "123";
String loc;
public ForeService(){}
#Override
public void onCreate() {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
mLocationCallback = new LocationCallback(){
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
//Do Location Work You Want To Do
onNewLocation(locationResult.getLastLocation());
}
};
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(3*1000);
mLocationRequest.setFastestInterval(1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
getLastLocation();
HandlerThread handlerThread = new HandlerThread("HANDLER");
handlerThread.start();
mServiceHandler = new Handler(handlerThread.getLooper());
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
CharSequence name = "Name Charseq";
if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.O){
NotificationChannel notificationChannel = new
NotificationChannel(CHANNEL_ID,name, NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(notificationChannel);
}
startForeground(NOTIFICATION_ID,getNotification());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// startForeground(NOTIFICATION_ID,getNotification());
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
mServiceHandler.removeCallbacksAndMessages(null);
//stopForeground(true);
}
#androidx.annotation.Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void getLastLocation() {
try {
mFusedLocationClient.getLastLocation()
.addOnCompleteListener(new OnCompleteListener<Location>() {
#Override
public void onComplete(#NonNull Task<Location> task) {
if (task.isSuccessful() && task.getResult() != null) {
mLocation = task.getResult();
} else {
Log.d(TAG, "Failed to get location.");
}
}
});
} catch (SecurityException unlikely) {
Log.d(TAG, "Lost location permission." + unlikely);
}
}
private void onNewLocation(Location location) {
mLocation = location;
if(mLocation==null)
Log.d("DSK_OPER","Lat: = "+"Not known");
else
Log.d("DSK_OPER"," : "+location.getLatitude());
//send intent to broadcast reciever
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtra(EXTRA_LOCATION, location);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
// todo - Write More code here
mNotificationManager.notify(NOTIFICATION_ID,getNotification());
}
private Notification getNotification() {
//Intent intent = new Intent(this,MainActivity.class);
if(mLocation==null)
loc = "unknown loc";
else
loc = String.valueOf(mLocation.getLatitude());
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle("Latitude and longitude")
.setContentText(" = "+loc)
.setOngoing(true)
.setPriority(Notification.PRIORITY_HIGH)
.setSmallIcon(R.mipmap.ic_launcher)
.setWhen(System.currentTimeMillis());
// Set the Channel ID for Android O.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID);
}
return builder.build();
}
}
I am using the foreground service for location update after some interval. Foreground service is useful when you wants to update the location if your app is in background but not killed. But this is your responsibility to stop the foreground service when your app is killed. So you just implement the LifeCycleDelegate so that you can start the service when app is in background and stop the service when app is in foreground. and also kill the service when your main activity or HomeActivity is killed.
this is the code for AppLifecycleHandler.
internal class AppLifecycleHandler(private val lifeCycleDelegate: LifeCycleDelegate) : Application.ActivityLifecycleCallbacks,
ComponentCallbacks2 {
private var appInForeground = false
override fun onActivityPaused(p0: Activity?) {}
/**
* app resumed
*/
override fun onActivityResumed(p0: Activity?) {
if (!appInForeground) {
appInForeground = true
lifeCycleDelegate.onAppForegrounded()
}
}
override fun onActivityStarted(p0: Activity?) {
}
override fun onActivityDestroyed(p0: Activity?) {
}
override fun onActivitySaveInstanceState(p0: Activity?, p1: Bundle?) {
}
override fun onActivityStopped(p0: Activity?) {
}
override fun onActivityCreated(p0: Activity?, p1: Bundle?) {
}
override fun onLowMemory() {}
override fun onConfigurationChanged(p0: Configuration?) {}
/**
* app sent to background
*/
override fun onTrimMemory(level: Int) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
appInForeground = false
lifeCycleDelegate.onAppBackgrounded()
}
}
Add this method to your application class
private fun registerLifecycleHandler(lifeCycleHandler: AppLifecycleHandler) {
registerActivityLifecycleCallbacks(lifeCycleHandler)
registerComponentCallbacks(lifeCycleHandler)
}
override fun getLifecycle(): Lifecycle {
return mLifecycleRegistry
}
implements LifeCycleDelegate in your application class and override the methods
internal interface LifeCycleDelegate {
fun onAppBackgrounded()
fun onAppForegrounded()
}
create app object in application class
val lifeCycleHandler = AppLifecycleHandler(this)
registerLifecycleHandler(lifeCycleHandler)
MainActivity.java
import android.Manifest;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements LocationListener {
PendingIntent mGeofencePendingIntent;
public static final int CONNECTION_FAILURE_RESOLUTION_REQUEST = 100;
private List<Geofence> mGeofenceList;
private GoogleApiClient mGoogleApiClient;
public static final String TAG = "Activity";
LocationRequest mLocationRequest;
double currentLatitude =12.9141 , currentLongitude = 77.6233;
Boolean locationFound;
protected LocationManager locationManager;
protected LocationListener locationListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (savedInstanceState == null) {
mGeofenceList = new ArrayList<Geofence>();
int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resp == ConnectionResult.SUCCESS) {
initGoogleAPIClient();
createGeofences(currentLatitude, currentLongitude);
} else {
Log.e(TAG, "Your Device doesn't support Google Play Services.");
}
// Create the LocationRequest object
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(1 * 1000) // 10 seconds, in milliseconds
.setFastestInterval(1 * 1000); // 1 second, in milliseconds
}
}
public void initGoogleAPIClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(connectionAddListener)
.addOnConnectionFailedListener(connectionFailedListener)
.build();
mGoogleApiClient.connect();
}
private GoogleApiClient.ConnectionCallbacks connectionAddListener =
new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
Log.i(TAG, "onConnected");
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location == null) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, MainActivity.this);
} else {
//If everything went fine lets get latitude and longitude
currentLatitude = location.getLatitude();
currentLongitude = location.getLongitude();
Log.i(TAG, currentLatitude + " WORKS " + currentLongitude);
//createGeofences(currentLatitude, currentLongitude);
//registerGeofences(mGeofenceList);
}
try{
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
getGeofencingRequest(),
getGeofencePendingIntent()
).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
Log.i(TAG, "Saving Geofence");
} else {
Log.e(TAG, "Registering geofence failed: " + status.getStatusMessage() +
" : " + status.getStatusCode());
}
}
});
} catch (SecurityException securityException) {
// Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
Log.e(TAG, "Error");
}
}
#Override
public void onConnectionSuspended(int i) {
Log.e(TAG, "onConnectionSuspended");
}
};
private GoogleApiClient.OnConnectionFailedListener connectionFailedListener =
new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.e(TAG, "onConnectionFailed");
}
};
/**
* Create a Geofence list
*/
public void createGeofences(double latitude, double longitude) {
String id = UUID.randomUUID().toString();
Geofence fence = new Geofence.Builder()
.setRequestId(id)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
.setCircularRegion(latitude, longitude, 200)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build();
mGeofenceList.add(fence);
}
private GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL);
builder.addGeofences(mGeofenceList);
return builder.build();
}
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
// calling addGeofences() and removeGeofences().
return PendingIntent.getService(this, 0, intent, PendingIntent.
FLAG_UPDATE_CURRENT);
}
#Override
public void onLocationChanged(Location location) {
currentLatitude = location.getLatitude();
currentLongitude = location.getLongitude();
Log.i(TAG, "onLocationChanged");
}
}
GeoTransitionsIntentservices.java
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingEvent;
public class GeofenceTransitionsIntentService extends IntentService {
private static final String TAG = "GeofenceTransitions";
public GeofenceTransitionsIntentService() {
super("GeofenceTransitionsIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i(TAG, "onHandleIntent");
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
//String errorMessage = GeofenceErrorMessages.getErrorString(this,
// geofencingEvent.getErrorCode());
Log.e(TAG, "Goefencing Error " + geofencingEvent.getErrorCode());
return;
}
// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
Log.i(TAG, "geofenceTransition = " + geofenceTransition + " Enter : " + Geofence.GEOFENCE_TRANSITION_ENTER + "Exit : " + Geofence.GEOFENCE_TRANSITION_EXIT);
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL){
showNotification("Entered the location", "Entered the Location");
}
else if(geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
Log.i(TAG, "Showing Notification...");
showNotification("Exited", "Exited the Location");
} else {
// Log the error.
showNotification("Error", "Error");
Log.e(TAG, "Error ");
}
}
public void showNotification(String text, String bigText) {
// 1. Create a NotificationManager
NotificationManager notificationManager =
(NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
// 2. Create a PendingIntent for AllGeofencesActivity
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// 3. Create and send a notification
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Geofence Monitor")
.setContentText(text)
.setContentIntent(pendingNotificationIntent)
.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.build();
notificationManager.notify(0, notification);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
new NotificationCompat.Builder(this).setSmallIcon(R.mipmap.ic_launcher);
} else {
new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher);
}
}
}
I want to implement Dwell when user exits the region. I have developed the geofence notification when user exits and entry the region and now I want to implement it has to monitor for a certain period of time and after exiting.
It should make toast message and if I implement I am getting error message.
In your IntentService use toaster like this, it will not give error
private void sendNotification(String notificationDetails) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "here is your toast msg", Toast.LENGTH_LONG).show();
}
});
// Create an explicit content Intent that starts the main Activity.
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
// Construct a task stack.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Add the main Activity to the task stack as the parent.
stackBuilder.addParentStack(MainActivity.class);
// Push the content Intent onto the stack.
stackBuilder.addNextIntent(notificationIntent);
// Get a PendingIntent containing the entire back stack.
PendingIntent notificationPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
// Get a notification builder that's compatible with platform versions >= 4
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// Define the notification settings.
builder.setSmallIcon(R.drawable.ic_launcher)
// In a real app, you may want to use a library like Volley
// to decode the Bitmap.
.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher))
.setColor(Color.RED)
.setContentTitle(notificationDetails)
.setContentText(getString(R.string.geofence_transition_notification_text))
.setContentIntent(notificationPendingIntent);
// Dismiss notification once the user touches it.
builder.setAutoCancel(true);
// Get an instance of the Notification manager
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Issue the notification
mNotificationManager.notify(0, builder.build());
}
And in
#Override
protected void onHandleIntent(Intent intent) {
// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
// Test that the reported transition was of interest.
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT){
sendNotification("your notification details");
sendAPICall("some token","body","my call");
}
}
//// Api call
public void sendAPICall(final String reg_token, final String body, final String title) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
OkHttpClient client = new OkHttpClient();
JSONObject json = new JSONObject();
JSONObject dataJson = new JSONObject();
dataJson.put("body", body);
dataJson.put("title", title);
json.put("notification", dataJson);
json.put("to", reg_token);
RequestBody body = RequestBody.create(JSON, json.toString());
Request request = new Request.Builder()
.header("Authorization", "key=" + Constants.GCM_AUTH_KEY)
.url("http://learnologic.com/send")
.post(body)
.build();
okhttp3.Response response = client.newCall(request).execute();
String finalResponse = response.body().string();
Logger.showDebugLog(finalResponse);
} catch (Exception e) {
Logger.showErrorLog(e.toString());
}
return null;
}
}.execute();
}
// stop monitor geofence
public void stopMonitoringGeofences() {
if (!mGoogleApiClient.isConnected()) {
Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show();
return;
}
try {
// Remove geofences.
LocationServices.GeofencingApi.removeGeofences(
mGoogleApiClient,
// This is the same pending intent that was used in addGeofences().
getGeofencePendingIntent()
).setResultCallback(this); // Result processed in onResult().
} catch (SecurityException securityException) {
// Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
logSecurityException(securityException);
}
}
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}