I have a Handler in my fragment that every 5 second add my location in SQLite.
What I need is to maintain this action in background when I open another fragment. How can I do this ?
Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {
#Override
public void run() {
long millis = System.currentTimeMillis() - startTime;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
Point p = ls.getPoint();
String X = Double.toString(p.getX());
String Y = Double.toString(p.getY());
db = new MySQLiteHelper(getActivity());
db.addLocation(new LocationXY(1,X,Y,"111"));
clearmap();
createPolyline();
timerHandler.postDelayed(this, 5000);
}
};
Well, what do both your fragments have in common? Their Activity! So run your handler in it.
As #Piyush pointed out, you should use an AsyncTask or a Service for background processes.
Yeah, as #Piyush and #Ricardo suggest - use a service with a Timer and a LocationListener.
Then invoke timer.scheduleAtFixedRate(yourSqliteUpdateMethod, 0, 5000); to update the database. See my service class below, which contains a lot of extra stuff, but you should be able to get what you want out of it...
public class TrackService extends Service {
/**
* Edit these values to adjust the LOCATION UPDATE polling interval
*/
private static final int LOCATION_INTERVAL_SECONDS = 5;
private static final float LOCATION_DISTANCE_METERS = 10f;
// Static timer variables
private static final long ONE_SECOND_IN_MILLIESECONDS = 1000;
// Create timer and set time as zero
private static Timer timer;
private long timeAsLong = 0;
/**
* Track and submission specific variables
*/
private List<LocationTrack> trackArrayList = new ArrayList<>();
private SubmissionSession submissionSession;
private long submissionID;
private boolean originAdded;
/**
* LocationManager and listener variables
*/
private LocationManager mLocationManager = null;
private LocationListener[] mLocationListeners = new LocationListener[]{
new LocationListener(LocationManager.GPS_PROVIDER)
};
/*private LocationListener[] mLocationListeners = new LocationListener[]{
new LocationListener(LocationManager.GPS_PROVIDER),
new LocationListener(LocationManager.PASSIVE_PROVIDER)
};*/
#Override
public void onCreate() {
//Log.d("onCreate");
timer = new Timer();
initializeLocationManager();
try {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, (LOCATION_INTERVAL_SECONDS * 1000), LOCATION_DISTANCE_METERS,
mLocationListeners[0]);
}
} catch (java.lang.SecurityException ex) {
Log.e("fail to request gps location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.e("gps provider does not exist, " + ex.getMessage());
}
/*try {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
mLocationManager.requestLocationUpdates(
LocationManager.PASSIVE_PROVIDER, (LOCATION_INTERVAL_SECONDS * 1000), LOCATION_DISTANCE_METERS,
mLocationListeners[1]);
}
} catch (java.lang.SecurityException ex) {
Log.e("fail to request passive location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.e("passive provider does not exist " + ex.getMessage());
}*/
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
submissionID = prefs.getLong("SUBMISSION_ID", 0);
submissionSession = new SubmissionSession(submissionID);
trackArrayList = new Select().from(LocationTrack.class).where(Condition.column(LocationTrack$Table.SUBMISSIONID).eq(submissionID)).queryList();
originAdded = false;
timeAsLong = prefs.getLong("ELAPSED_TIME_AS_LONG", 0);
} else {
submissionID = intent.getLongExtra("SUB_ID", 0);
submissionSession = new SubmissionSession(submissionID);
// Stick in a reasonably sensible location to indicate to rest of app that a track's
// being captured - this is deleted on 1st real data point from GPS captured
trackArrayList.add(setLocationToLocationTrack(submissionSession.getLocation().getLocation()));
originAdded = true;
timeAsLong = intent.getLongExtra("START_TIME", 0);
}
// Start schedule at one second intervals, calling MainTask
timer.scheduleAtFixedRate(new TrackService.updateTimerAndBroadcastTask(), 0, ONE_SECOND_IN_MILLIESECONDS);
showNotification();
//super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
/**
* This method is necessary to increase the priority of the background services. It appeared that
* some devices, when running eBird Tracks and then used camera, the Tracks service was killed
* by Android due to limited resources. Upping the priority should reduce this.
*/
private void showNotification() {
final int NOTIFICATION_FLAG = 101;
final int ICON_SCALE_DIMENSION = 128;
Intent mainIntent = getPackageManager()
.getLaunchIntentForPackage(getPackageName())
.setPackage(null)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.lab_logo_square);
PendingIntent pendingMainIntent = PendingIntent.getActivity(this, 0,
mainIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_kiwi_bird1600)
.setLargeIcon(Bitmap.createScaledBitmap(icon, ICON_SCALE_DIMENSION, ICON_SCALE_DIMENSION, false))
.setContentText(getString(R.string.location_and_timer_services_running))
.setContentTitle(getString(R.string.app_name))
.setContentIntent(pendingMainIntent)
.setOngoing(true)
.build();
startForeground(NOTIFICATION_FLAG, notification);
}
#Override
public void onDestroy() {
// Need to call cancel AND purge when the user stops the list
timer.cancel();
timer.purge();
if (mLocationManager != null) {
for (int i = 0; i < mLocationListeners.length; i++) {
try {
mLocationManager.removeUpdates(mLocationListeners[i]);
} catch (Exception ex) {
Log.e("fail to remove location listeners, ignore", ex);
}
}
}
submissionSession.setTracks(trackArrayList);
super.onDestroy();
}
private void initializeLocationManager() {
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
private class LocationListener implements android.location.LocationListener {
Location mLastLocation;
public LocationListener(String provider) {
mLastLocation = new Location(provider);
}
#Override
public void onLocationChanged(Location location) {
mLastLocation.set(location);
// Code to add existing location object of site to tracks database on initiation
// to at least give one sensible point. Necessary for assigning the submission as
// an active recording - if no GPS picked up it was being overlooked in the methods
// used to display Checklist in progress throughout app. On first real capture of
// track data, it's deleted
if (originAdded) {
LocationTrack t = trackArrayList.get(0);
t.delete();
trackArrayList.remove(0);
originAdded = false;
}
trackArrayList.add(setLocationToLocationTrack(location));
sendDistanceToSubmissionActivity(Utils.routeDistance((ArrayList) trackArrayList));
}
#Override
public void onProviderDisabled(String provider) {
//Log.d("onProviderDisabled: " + provider);
}
#Override
public void onProviderEnabled(String provider) {
//Log.d("onProviderEnabled: " + provider);
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
//Log.d("onStatusChanged: " + provider);
}
}
// Send an Intent with an action named "tracks_update".
protected void sendDistanceToSubmissionActivity(double distance) {
Intent intent = new Intent("tracks_update");
intent.putExtra("distance", distance);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
/**
* Method to create Locationtrack object and save it to SQLite
*
* #param loc
* #return
*/
protected LocationTrack setLocationToLocationTrack(Location loc) {
LocationTrack l = new LocationTrack();
l.setSubmissionID(submissionID);
l.setLat(loc.getLatitude());
l.setLon(loc.getLongitude());
l.setAccuracy(loc.getAccuracy());
l.setTime(System.currentTimeMillis());
l.setValid(-1); // -1 signifies that the track is active (0 = invalid after editing, 1 = valid)
//Log.d(l.getLat() + " " + l.getLon() + " saved()");
l.save();
return l;
}
/**
* Inner main task, adds a second onto timeAsLong, then sends to SubmissionActivity
*/
private class updateTimerAndBroadcastTask extends TimerTask {
public void run() {
timeAsLong += ONE_SECOND_IN_MILLIESECONDS;
sendDistanceToSubmissionActivity(timeAsLong);
}
// Send an Intent with an action named "timer_update".
protected void sendDistanceToSubmissionActivity(long time) {
//Log.d();
Intent intent = new Intent("timer_update");
intent.putExtra("long_time", time);
LocalBroadcastManager.getInstance(TrackService.this).sendBroadcast(intent);
}
}
}
Related
I want to track the user location. So I have made a code so that on every 10 mins of interval its location should be hit on server. If it is not moving also the location should hit at 10 min of interval. I set Location interval minutes for 10 min public static final long LOCATION_INTERVAL_MINUTES = 10 * 60 * 1000;
It should update the location with 10 min, but it hits randomly. I also increase the gps frequency distance from 10 to gpsFreqInDistance = 100 .Random hits are stopped but it will not updated on 10 mins of interval.
So what to do to get location at 10 min of interval.
Below is my manifiest
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
As for Android above 10version u cant use background location and foreground location simultaneously. So i comment the ACCESS_BACKGROUND_LOCATION" in my project.
public class LocationMonitoringService implements LocationListener, GpsStatus.Listener {
private static final String TAG = LocationMonitoringService.class.getSimpleName();
private Context mContext;
private LocationRepository mLocationRepository;
private ShareLocationAdapterClass mAdapter;
private SyncOfflineRepository syncOfflineRepository;
private long updatedTime = 0;
private List<UserLocationPojo> mUserLocationPojoList;
private SyncOfflineAttendanceRepository syncOfflineAttendanceRepository;
public LocationMonitoringService(final Context context) {
mContext = context;
mLocationRepository = new LocationRepository(AUtils.mainApplicationConstant.getApplicationContext());
syncOfflineRepository = new SyncOfflineRepository(AUtils.mainApplicationConstant.getApplicationContext());
syncOfflineAttendanceRepository = new SyncOfflineAttendanceRepository(AUtils.mainApplicationConstant.getApplicationContext());
mUserLocationPojoList = new ArrayList<>();
mAdapter = new ShareLocationAdapterClass();
mAdapter.setShareLocationListener(new ShareLocationAdapterClass.ShareLocationListener() {
#Override
public void onSuccessCallBack(boolean isAttendanceOff) {
if (isAttendanceOff && !syncOfflineAttendanceRepository.checkIsAttendanceIn()) {
AUtils.setIsOnduty(false);
((MyApplication) AUtils.mainApplicationConstant).stopLocationTracking();
}
}
#Override
public void onFailureCallBack() {
}
});
}
public void onStartTacking() {
LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
//Exception thrown when GPS or Network provider were not available on the user's device.
try {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setPowerRequirement(Criteria.NO_REQUIREMENT);
criteria.setAltitudeRequired(false);
criteria.setSpeedRequired(true);
criteria.setCostAllowed(false);
criteria.setBearingRequired(false);
//API level 9 and up
criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);
int gpsFreqInDistance = 100;
assert locationManager != null;
locationManager.addGpsStatusListener(this);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, AUtils.LOCATION_INTERVAL,
gpsFreqInDistance, this, null);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, AUtils.LOCATION_INTERVAL,
gpsFreqInDistance, this, null);
} catch (IllegalArgumentException | SecurityException e) {
Log.e(TAG, Objects.requireNonNull(e.getLocalizedMessage()));
Log.d(TAG, "onStartTacking: " + e.getMessage());
Log.d(TAG, "onStartTacking: " + e.getLocalizedMessage());
e.printStackTrace();
} catch (RuntimeException e) {
Log.e(TAG, Objects.requireNonNull(e.getLocalizedMessage()));
Log.d(TAG, "onStartTacking: " + e.getMessage());
e.printStackTrace();
}
}
public void onStopTracking() {
mAdapter.shareLocation();
LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
locationManager.removeUpdates(this);
}
/*
* LOCATION CALLBACKS
*/
//to get the location change
#Override
public void onLocationChanged(Location location) {
Log.d("okh ", "onLocationChanged: "+System.currentTimeMillis());
if (location != null) {
Log.d(TAG, String.valueOf(location.getAccuracy()));
if (!AUtils.isNullString(String.valueOf(location.getLatitude())) && !AUtils.isNullString(String.valueOf(location.getLongitude()))) {
Prefs.putString(AUtils.LAT, String.valueOf(location.getLatitude()));
Prefs.putString(AUtils.LONG, String.valueOf(location.getLongitude()));
if (Prefs.getBoolean(AUtils.PREFS.IS_ON_DUTY, false)) {
if (updatedTime == 0) {
updatedTime = System.currentTimeMillis();
Log.d(TAG, "updated Time ==== " + updatedTime);
}
if ((updatedTime + AUtils.LOCATION_INTERVAL_MINUTES) <= System.currentTimeMillis()) {
updatedTime = System.currentTimeMillis();
Log.d(TAG, "updated Time ==== " + updatedTime);
}
sendLocation();
}
}
} else {
Log.d(TAG, "onLocationChanged: no location found !!");
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.d(TAG, "onStatusChanged" + provider + "Status" + status);
}
#Override
public void onProviderEnabled(String provider) {
Log.d(TAG, " onProviderEnabled" + provider);
}
#Override
public void onProviderDisabled(String provider) {
Log.d(TAG, " onProviderDisabled" + provider);
}
#Override
public void onGpsStatusChanged(int event) {
}
private void sendLocation() {
// mAdapter.shareLocation(getTempList());
Log.d("okh", "sendLocation: Current Time In Millies "+ System.currentTimeMillis());
try {
Calendar CurrentTime = AUtils.getCurrentTime();
Calendar DutyOffTime = AUtils.getDutyEndTime();
if (CurrentTime.before(DutyOffTime)) {
Log.i(TAG, "Before");
UserLocationPojo userLocationPojo = new UserLocationPojo();
userLocationPojo.setUserId(Prefs.getString(AUtils.PREFS.USER_ID, ""));
userLocationPojo.setLat(Prefs.getString(AUtils.LAT, ""));
userLocationPojo.setLong(Prefs.getString(AUtils.LONG, ""));
double startLat = Double.parseDouble(Prefs.getString(AUtils.LAT, "0"));
double startLng = Double.parseDouble(Prefs.getString(AUtils.LONG, "0"));
userLocationPojo.setDistance(String.valueOf(AUtils.calculateDistance(
AUtils.mainApplicationConstant.getApplicationContext(), startLat, startLng)));
// userLocationPojo.setDatetime(AUtils.getServerDateTime()); //TODO
userLocationPojo.setDatetime(AUtils.getServerDateTimeLocal());
userLocationPojo.setOfflineId("0");
if (AUtils.isInternetAvailable() && AUtils.isConnectedFast(mContext))
userLocationPojo.setIsOffline(true);
else
userLocationPojo.setIsOffline(false);
String UserTypeId = Prefs.getString(AUtils.PREFS.USER_TYPE_ID, AUtils.USER_TYPE.USER_TYPE_GHANTA_GADI);
if (AUtils.isInternetAvailable()) {
TableDataCountPojo.LocationCollectionCount count = syncOfflineRepository.getLocationCollectionCount(AUtils.getLocalDate());
if ((UserTypeId.equals(AUtils.USER_TYPE.USER_TYPE_GHANTA_GADI) || UserTypeId.equals(AUtils.USER_TYPE.USER_TYPE_WASTE_MANAGER))
&& (count.getLocationCount() > 0 || count.getCollectionCount() > 0)) {
syncOfflineRepository.insetUserLocation(userLocationPojo);
} else {
mUserLocationPojoList.add(userLocationPojo);
mAdapter.shareLocation(mUserLocationPojoList);
mUserLocationPojoList.clear();
}
} else {
if (UserTypeId.equals(AUtils.USER_TYPE.USER_TYPE_EMP_SCANNIFY)) {
Type type = new TypeToken<UserLocationPojo>() {
}.getType();
mLocationRepository.insertUserLocationEntity(new Gson().toJson(userLocationPojo, type));
} else {
syncOfflineRepository.insetUserLocation(userLocationPojo);
}
mUserLocationPojoList.clear();
}
}
else {
Log.i(TAG, "After");
syncOfflineAttendanceRepository.performCollectionInsert(mContext,
syncOfflineAttendanceRepository.checkAttendance(), AUtils.getCurrentDateDutyOffTime());
AUtils.setIsOnduty(false);
((MyApplication) AUtils.mainApplicationConstant).stopLocationTracking();
Activity activity = ((Activity) AUtils.currentContextConstant);
if (activity instanceof DashboardActivity) {
((Activity) AUtils.currentContextConstant).recreate();
AUtils.DutyOffFromService = true;
}
if (!AUtils.isNull(AUtils.currentContextConstant)) {
((Activity) AUtils.currentContextConstant).recreate();
}
}
} catch (Exception e) {
e.printStackTrace();
}
Below is the foreground service which i have used
public class ForgroundService extends Service {
private LocationMonitoringService monitoringService;
private Handler locationHandler;
private Runnable locationThread;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
// Toast.makeText(this, " MyService Created ", Toast.LENGTH_LONG).show();
Log.d("okh", "onCreate: " +System.currentTimeMillis());
monitoringService = new LocationMonitoringService(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Toast.makeText(this, " MyService Started", Toast.LENGTH_LONG).show();
Log.d("okh", "onCreate: " +System.currentTimeMillis());
Log.d("okh", "onStartCommand: ");
final int currentId = startId;
locationThread = new Runnable() {
public void run() {
monitoringService.onStartTacking();
}
};
locationHandler = new Handler(Looper.getMainLooper());
locationHandler.post(locationThread);
startLocationForeground();
return Service.START_STICKY;
// return Service.START_NOT_STICKY;
}
#Override
public void onDestroy() {
//Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show();
locationHandler.removeCallbacks(locationThread);
monitoringService.onStopTracking();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
stopForeground(STOP_FOREGROUND_REMOVE);
}
if(Prefs.getBoolean(AUtils.PREFS.IS_ON_DUTY,false))
{
Intent broadcastIntent = new Intent(this, RestarterBroadcastReceiver.class);
sendBroadcast(broadcastIntent);
}
}
private void startLocationForeground() {
if (Build.VERSION.SDK_INT >= 26) {
String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? this.createNotificationChannel("my_service", "My Background Service")
: "";
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder((Context) this,
channelId);
Notification notification = notificationBuilder
.setOngoing(true)
.setContentText("Please don't kill the app from background. Thank you!!")
.setSmallIcon(getNotificationIcon(notificationBuilder))
.setPriority(-2)
.setCategory("service")
.build();
startForeground(101, notification);
}
}
#RequiresApi(26)
private String createNotificationChannel(String channelId, String channelName) {
NotificationChannel chan = new NotificationChannel(channelId, (CharSequence)channelName,
NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
service.createNotificationChannel(chan);
return channelId;
}
private int getNotificationIcon(NotificationCompat.Builder notificationBuilder) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int color = 0x008000;
notificationBuilder.setColor(color);
return R.drawable.ic_noti_icon;
}
return R.drawable.ic_noti_icon;
}
}
Why you are passing null in the last parameter? Did you try without passing that?
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, AUtils.LOCATION_INTERVAL,
gpsFreqInDistance, this, null);
First of all Google Location Services are far better than Traditional LocationManager to achieve this requirement.
https://developer.android.com/training/location/request-updates
I am new to android and am working on an Android app that makes measurements every couple of seconds and links these measurements to the current location of the user. The data should still be collected even when the user is using their phone (app is minimized) or when the screen is locked. I should note that this app is meant to be used internally (not on the Google Play Store) and with full permission of the users!
When looking in the Android documentation I found that a foreground service might be the solution for me. So I implemented this service the way it is documented. When programming the app everything seemed to be working fine but when I started to make a release build of the application and was testing the app I noticed some unexpected behavior. This is the code i used for my service and to call the service from my activity:
public class LocationService extends Service {
private final LocationServiceBinder binder = new LocationServiceBinder();
private final String TAG = "LocationService";
private LocationListener mLocationListener;
private LocationManager mLocationManager;
private PowerManager.WakeLock wakeLock;
private Timer timer;
private final int LOCATION_INTERVAL = 500;
private final int LOCATION_DISTANCE = 1;
#Override
public void onCreate() {
startForeground(12345678, getNotification());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Service:WakeLock");
wakeLock.acquire();
startTracking();
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public void onTaskRemoved(Intent rootIntent) {
task.cancel();
wakeLock.release();
if (timer != null) {
timer.cancel();
timer.purge();
timer.cancel();
timer = null;
}
if (mLocationManager != null) {
try {
mLocationManager.removeUpdates(mLocationListener);
} catch (Exception ex) {
Log.i(TAG, "fail to remove location listeners, ignore", ex);
}
}
stopForeground(true);
stopSelf();
super.onTaskRemoved(rootIntent);
}
private Notification getNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("channel_01",
"My Channel", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, intent, 0);
return new NotificationCompat.Builder(this, "channel_01")
.setContentTitle("APP")
.setContentText("App is running!")
.setContentIntent(pendingIntent).build();
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class LocationServiceBinder extends Binder {
public LocationService getService() {
return LocationService.this;
}
}
private TimerTask task = new TimerTask() {
#Override
public void run() {
requestLocationInformation();
}
};
public void startTracking() {
initializeLocationManager();
mLocationListener = new LocationListener(LocationManager.GPS_PROVIDER);
if (timer == null) {
timer = new Timer("Measurements");
timer.scheduleAtFixedRate(task, 1000, 5000);
}
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
LOCATION_INTERVAL, LOCATION_DISTANCE, mLocationListener);
} 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());
}
}
}
private class LocationListener implements android.location.LocationListener {
private Location lastLocation = null;
private final String TAG = "LocationListener";
private Location mLastLocation;
public LocationListener(String provider) {
mLastLocation = new Location(provider);
}
}
public void requestLocationUpdates() {
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
LOCATION_INTERVAL, LOCATION_DISTANCE, mLocationListener);
} 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());
}
}
private void initializeLocationManager() {
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
private void requestLocationInformation() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
Location location = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null && swbRunning) {
String locInfo = "latitude:" + location.getLatitude() + " longitude:" + location.getLongitude() + " accuracy: " + location.getAccuracy() + "\n";
Log.d(TAG, "Location --> " + locInfo);
}
}
}
}
public class MainActivity extends ReactActivity {
//permissions to be requested at runtime - needed for android 6.0+
static String[] runtimePermissions = { Manifest.permission.INTERNET, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE, Manifest.permission.WAKE_LOCK };
static int permissionRequestCode = 1;
private LocationService locaService;
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!havePermissions()) {
requestPermissions(runtimePermissions, permissionRequestCode);
} else {
Intent intent = new Intent(this, LocationService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForegroundService(intent);
else
startService(intent);
}
}
private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
locaService = ((LocationService.LocationServiceBinder) service).getService();
}
public void onServiceDisconnected(ComponentName className) {
locaService = null;
}
};
To test the app I used a Samsung Galaxy A42 (android 11) device, this is the device that is also used by our users.
In the release version, everything works fine until the app is minimized or the screen is locked.
I looked into this issue and based on information I could find online about this problem,
I tried some different solutions. I added a partial wake lock to keep the device running smoothly and I also added stopWithTask=false to the android manifest as suggest by the following posts. I also ran the application on an older device (android 8) that did not have this problem.
Keep background service running after killing an application
My Android 11 app stops running when the user puts it into the background unless the phone is connected to power
Android keep screen on on Samsung devices
As mentioned in that last post Samsung is know for performing battery optimizations which may affect apps running foreground services. So I used the advice on https://dontkillmyapp.com/ but this doesn’t solve the issue.
Since I tried every solution I could find, I am starting to think that this might be the main source of my issue. I was wondering if anyone has had a similar issue and if there might be a solution for this problem.
So it works fine when the app is foreground and the first minute in the background. But after that the listener stops receiving gyro data. But the post thread and location listener keep working.
I tried of lot of things and I got nothing to work.
public final class Secret extends Service implements SensorEventListener, LocationListener {
private final String create = "CREATE TABLE IF NOT EXISTS data(lat float, lon float, x float, y float, z float, timestamp bigint)";
private SensorManager sensorManager;
private Database database;
private Location location;
#Override
public final void onCreate() {
/* Init sensor manager */
this.sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
}
#Override
public final int onStartCommand(final Intent intent, final int flags, final int startId) {
/* Initialize & load database */
this.database = new Database(openOrCreateDatabase("name_data.db", MODE_PRIVATE, null));
this.database.exec(this.create);
/* Init location manager */
final LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
try {
/* Register sensor change listener */
this.sensorManager.registerListener(this, this.sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY), SensorManager.SENSOR_STATUS_ACCURACY_HIGH);
/* Register location change listener */
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 1, this);
} catch (final SecurityException e) {
Toast.makeText(this, "Failed to start: " + e.getMessage(), Toast.LENGTH_LONG).show();
e.printStackTrace();
}
/* Post data every 5 minutes */
final Thread thread = new Thread(new Runnable() {
#Override
public final void run() {
/* Secret instance */
final Secret secret = Secret.this;
/* 5 minutes */
final long time = 15000;
while (true) {
try {
/* Wait 5 minutes */
Thread.sleep(time);
/*
* Unregister sensor change listener,
* this way posting wont cause any database errors,
* and the sensor events wont time out when app is running in the background
*/
sensorManager.unregisterListener(secret);
/* Post database */
final int returned = post();
/* Register sensor change listener */
sensorManager.registerListener(secret, sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), SensorManager.SENSOR_STATUS_ACCURACY_HIGH);
switch (returned) {
case -1:
System.err.println("Database too small to post");
break;
case HttpURLConnection.HTTP_OK:
System.out.println("Posted database");
break;
default:
System.err.println("Post failed: " + returned);
}
} catch (final InterruptedException e) {
System.err.println("Failed to sleep");
e.printStackTrace();
}
}
}
});
/* Set thread priority and start */
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
return START_STICKY;
}
#Nullable
#Override
public final IBinder onBind(final Intent intent) {
return null;
}
#Override
public final void onSensorChanged(final SensorEvent event) {
System.out.println(System.currentTimeMillis());
/* Store data if location is known */
if (event == null || this.location == null) return;
float[] gyro = event.values;
/* Insert data into database */
this.database.exec("INSERT INTO data(lat, lon, x, y, z, timestamp) VALUES(" + this.location.getLatitude() + "," + this.location.getLongitude() + "," + gyro[0] + "," + gyro[1] + "," + gyro[2] + "," + System.currentTimeMillis() + ")");
}
#Override
public final void onAccuracyChanged(final Sensor sensor, final int accuracy) {
}
#Override
public final void onLocationChanged(final Location location) {
this.location = location;
}
#Override
public final void onStatusChanged(final String provider, final int status, final Bundle extras) {
}
#Override
public final void onProviderEnabled(final String provider) {
}
#Override
public final void onProviderDisabled(final String provider) {
}
/**
* Post database to server
*/
private final int post() {
/* Check if database is large enough (8 MB) to be pushed */
if (this.database.getFile().length() <= 8192) return -1;
/* Push database to server */
final int returned = Connection.post(this.database.getFile());
/* Check if database was pushed successfully */
if (returned == HttpURLConnection.HTTP_OK) {
/* Clear database if it was pushed to server successfully */
this.database.close();
this.database.getFile().delete();
/* Create new database */
this.database = new Database(openOrCreateDatabase("name_data.db", MODE_PRIVATE, null));
this.database.exec(this.create);
}
return returned;
}
}
OnDestroy gets triggered and then the sensor stops giving data. the Thread and location keep running.
I have a service which tracks the user location and I am not sure how I can interact it with my activity. I need to be able to access the first available latitude & longitude values and also if there is a change, can anyone give me any tips please.
My service:
public class LocationService extends Service implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
public double latitude, longitude;
IBinder mBinder = new LocalBinder();
private LocationClient mLocationClient;
private LocationRequest mLocationRequest;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private Boolean servicesAvailable = false;
public class LocalBinder extends Binder {
public LocationService getServerInstance() {
return LocationService.this;
}
}
#Override
public void onCreate() {
super.onCreate();
mInProgress = false;
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
// Set the update interval to 5 seconds
mLocationRequest.setInterval(Constants.UPDATE_INTERVAL);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(Constants.FASTEST_INTERVAL);
servicesAvailable = servicesConnected();
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
mLocationClient = new LocationClient(this, this, this);
}
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
return true;
} else {
return false;
}
}
public int onStartCommand (Intent intent, int flags, int startId)
{
super.onStartCommand(intent, flags, startId);
if(!servicesAvailable || mLocationClient.isConnected() || mInProgress)
return START_STICKY;
setUpLocationClientIfNeeded();
if(!mLocationClient.isConnected() || !mLocationClient.isConnecting() && !mInProgress)
{
appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Started", Constants.LOG_FILE);
mInProgress = true;
mLocationClient.connect();
}
return START_STICKY;
}
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
private void setUpLocationClientIfNeeded()
{
if(mLocationClient == null)
mLocationClient = new LocationClient(this, this, this);
}
// Define the callback method that receives location updates
#Override
public void onLocationChanged(android.location.Location location) {
// Report to the UI that the location was updated
String msg = Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Log.d("debug", msg);
latitude = location.getLatitude();
longitude = location.getLongitude();
// Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
appendLog(msg, Constants.LOCATION_FILE);
}
#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
mInProgress = false;
if(servicesAvailable && mLocationClient != null) {
mLocationClient.removeLocationUpdates(this);
// Destroy the current location client
mLocationClient = 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()) + ": Stopped", Constants.LOG_FILE);
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
mLocationClient.requestLocationUpdates(mLocationRequest, this);
appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Connected", Constants.LOG_FILE);
}
/*
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
#Override
public void onDisconnected() {
// Turn off the request flag
mInProgress = false;
// Destroy the current location client
mLocationClient = 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 {
}
}
}
My broadcast receiver:
public class LocationLoggerServiceManager extends BroadcastReceiver {
public static final String TAG = "LocationLoggerServiceManager";
#Override
public void onReceive(Context context, Intent intent) {
// Make sure we are getting the right intent
if( "android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
ComponentName comp = new ComponentName(context.getPackageName(), LocationService.class.getName());
ComponentName service = context.startService(new Intent().setComponent(comp));
if (null == service){
// something really wrong here
Log.e(TAG, "Could not start service " + comp.toString());
}
}else {
Log.e(TAG, "Received unexpected intent " + intent.toString());
}
}
}
In your onLocationChanged callback, you need to use sendBroadcast to notify your Activity of the change. Here is an example from an answer to another question (taken from here https://stackoverflow.com/a/4739898/826731).
In your service, you can broadcast your intent like this:
sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));
(the RefreshTask.REFRESH_DATA_INTENT variable is simply a string)
And in your activity, you need to check for that particular string:
private class DataUpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(RefreshTask.REFRESH_DATA_INTENT)) {
//Do stuff - maybe update my view based on the changed DB contents
}
}
You can pass values from the service to the BroadcastReceiver as follows:
In the service
i.putExtra("Counter", counter);
In BroadcastReceiver
intent.getIntExtra("Counter", -1); // -1 is the default value
I'm a bit new to Android and I'm trying to move some location finding and volley methods out of my fragment into a service, but now sure where to put what. It's basically a weather app that gets your current location and uses volley to pull forecast.io api data.
Here is my fragment now:
public class WeatherListFragment extends ListFragment implements LocationListener {
private final String initialURL = "https://api.forecast.io/forecast/8fc2b0556e166fa4670d4014d318152a/";
Weather[] myWeatherArray = {};
Weather myWeatherObject;
WeatherAdapter weatherAdapter;
LocationManager mLocationManager;
String currentLoc;
JSONArray data;
JSONObject day;
#Override
public void onLocationChanged(Location location) {
// Called when a new location is found by the network location provider.
makeUseOfNewLocation(location);
// Remove the listener you previously added
mLocationManager.removeUpdates(this);
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLocationManager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
makeUseOfNewLocation(mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER));
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1, 1, this);
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1, 1, this);
}
public void getData() {
String API_URL = setLatLong(initialURL, currentLoc);
RequestQueue requestQueue = Volley.newRequestQueue(getActivity());
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, API_URL, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONObject daily = response.getJSONObject("daily");
data = daily.getJSONArray("data");
myWeatherArray = new Weather[data.length()];
for (int i = 0; i < myWeatherArray.length; i++) {
day = data.getJSONObject(i);
myWeatherObject = new Weather();
myWeatherObject.setmDate(day.getInt("time"));
myWeatherObject.setmTempMin(day.getInt("temperatureMin"));
myWeatherObject.setmTempMax(day.getInt("temperatureMax"));
myWeatherObject.setIcon(day.getString("icon"));
myWeatherArray[i] = myWeatherObject;
}
} catch (JSONException e) {
e.printStackTrace();
}
if (weatherAdapter != null) {
weatherAdapter.setData(myWeatherArray);
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getActivity(), "volley died", Toast.LENGTH_SHORT).show();
}
}
);
requestQueue.add(jsonObjectRequest);
}
public void makeUseOfNewLocation(Location location) {
if (location == null) {
return;
}
mLocationManager.removeUpdates(this);
double latDouble = location.getLatitude();
double longDouble = location.getLongitude();
String latString = String.valueOf(latDouble);
String longString = String.valueOf(longDouble);
String latLong = latString + "," + longString;
Log.e("gps", latLong);
currentLoc = latLong;
getData();
}
public String setLatLong(String roughURL, String loc) {
return roughURL + loc;
}
and here is the blank Service class using a Handler:
public class NotificationService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
Notification n = new Notification(getApplicationContext());
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Thread.MIN_PRIORITY);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}