How to deal with background geofencing in 2022? - java

Here is the situation:
When a user creates a Geofence, I save it to backend and I register a Geofence with the OS. But whenever my app restarts I fetch geofences from my backend and reregister them with the OS again, since they keep disappearing.
I have two classes MainActivity and FormActivity. Both of these activities register Geofences, so I have extracted the actual registration to an ordinary POJO Geofences.java
Here is the problem:
Now the strange thing is, triggers are only received when a map activity is on the screen. I do have a map activity in my app, but it doesn't even have to be my map-activity, even if I launch google maps geofence triggers start firing.
What am I doing wrong?
Geofences.java:
public class Geofences {
private final String TAG = Geofences.class.getSimpleName();
private final float RADIUS = 150.0F; //meter
private boolean success = false;
private final int LOITERING_IN_MILLISECONDS = 30000;// 30 seconds
public boolean doGeofenceStuff(GeoTemp newTemp, String geofenceId, PendingIntent pendingIntent, GeofencingClient geofencingClient) {
Geofence geofence = createGeofence(newTemp, geofenceId);
GeofencingRequest geofencingRequest = createGeofenceRequest(geofence);
geofencingClient.addGeofences(geofencingRequest, pendingIntent)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
success = true;
Log.i(TAG, "onComplete: DEBUG-Message: Geofence has been added.");
} else {
success = false;
Log.i(TAG, "onComplete: Geofence could not be added");
}
}
}); // handle error here
return success;
}
// Create a Geofence
private Geofence createGeofence(GeoTemp geoTemp, String geofenceId) {
long expiration = getExpirationForCurrentGeofence();
if (expiration < 1) {
Log.e(TAG, "createGeofence: Can't create Geofence, since expiration is less than zero");
return null;
}
Log.d(TAG, "createGeofence");
return new Geofence.Builder()
.setRequestId(geofenceId)
.setCircularRegion(getLat(), getLong(), RADIUS)
.setExpirationDuration(expiration)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_DWELL | Geofence.GEOFENCE_TRANSITION_EXIT)
.setLoiteringDelay(LOITERING_IN_MILLISECONDS)
.build();
}
// Create a Geofence Request
private GeofencingRequest createGeofenceRequest(Geofence geofence) {
Log.d(TAG, "createGeofenceRequest");
return new GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL)
.addGeofence(geofence)
.build();
}
}
This POJO Geofences.java is then used by two of my activities:
MainActivity:
public class MainActivity extends AppCompatActivity {
private static String TAG = "MainActivity";
private final int GEOFENCE_REQ_CODE = 0;
private GeofencingClient geofencingClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
geofencingClient = LocationServices.getGeofencingClient(this);
getCurrentTemps();
}
private void refreshGeofence(GeoTemp temp, String id) {
new Geofences().doGeofenceStuff(temp, id, createGeofencePendingIntent(), geofencingClient);
}
private void getCurrentTemps() {
List<GeoTemp> currentGeofences = getUpdatedList();
currentGeofences.forEach(geoTemp -> {
refreshGeofence( geoTemp, id);
});
}
private PendingIntent createGeofencePendingIntent() {
Log.d(TAG, "createGeofencePendingIntent");
Intent intent = new Intent(this, LocationAlertIntentService.class);
return PendingIntent.getService(
this, GEOFENCE_REQ_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
There is one more activity which uses Geofences.java to register geofences with the operating system.
Update:
I have found out that, if any other app (including mine) requests for location lock, geofence triggers fire. I need them to fire in the background.

I had similar issue when working with geofencing in android.
This happens due to background restrictions added in Android Oreo and later.
OS does not allow your app to start service when it is in background so you won't receive geofence trigger.
To handle this:
Add broadcast receiver to receive intent. (This receiver will get
geofence alert even when app is in background)
Replace service with JobIntentService. (This will use OS JobSchedular
and run even with background restrictions)
Get broadcast from pending intent instead of service.
Checkout this sample project for further clarifications.

Related

Why does foreground service stop working when device go into sleep mode

I want to create an app that is constantly checking for location change and put the current location in the firebase (e.g. an app for runners).
Unfortunately the foregroundservice is being stopped or paused every time the device go into sleep mode.
For starters I wanted to create a foreground service that is continuously writing information to the base (that would be a time stamp or a simple string) every second.
After some time it just stops writing to firebase without calling stopself().
The service is working fine on the emulator (even if put to sleep), but stops when tested on a real device – in my case Huawei, Android 8.1.0.
What should I do to force service to run in every state of the device?
My MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "start");
startForegroundService(intent);
}
else {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "start");
startService(intent);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "stop");
startForegroundService(intent);
}
else {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "stop");
startService(intent);
}
}
}
MyService:
public class MyService extends Service {
int i =0;
private String CHANNEL_ID = "2345";
#Override
public void onCreate() {
super.onCreate();
}
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(1000, createNotification());
String action = intent.getExtras().getString("action");
switch (action){
case "start":
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
myfunction();
handler.postDelayed(this, 1000);
}
};
break;
case "stop":
stopfunction();
break;
}
handler.postDelayed(runnable, 1000);
return START_NOT_STICKY;
}
private void stopfunction() {
stopSelf();
}
private void myfunction() {
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("locations");
myRef.child("location").setValue(i);
i++;
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
#RequiresApi(Build.VERSION_CODES.O)
private void createChannel(){
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, getString(R.string.infoTxt),
NotificationManager.IMPORTANCE_HIGH);
channel.setShowBadge(false);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
notificationManager.createNotificationChannel(channel);
}
private Notification createNotification(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
createChannel();
}
Intent notificationItent = new Intent(this, MainActivity.class);
notificationItent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(this, 0, notificationItent, 0);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setColor(ContextCompat.getColor(this, android.R.color.background_dark))
.setContentIntent(intent)
.setSmallIcon(R.drawable.ic_launcher_background)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOnlyAlertOnce(true)
.setContentTitle("GPS Location")
.build();
}
}
I've tried everything: service, foreground service, broadcast receiver, jobSheduler, WorkerManager – nothing helped. Then I found it’s a new HUAWEI feature called “power-intensive app monitor “. It kills every app that runs in the background for a long time unless user gives special permissions to it.
The path to do this:
Settings -> Security & privacy -> Location services -> recent location requests: YOUR APP NAME -> Battery -> uncheck Power-intensive prompt, App launch: Manage manually: check all three positions: Auto-launch, secondary launch, run in background.
I don’t know is there a way to do this programmatically. I think the best way is to create a sort of help activity and explain the user what to do if application won’t work.
Foreground services generally should be used for task which require user attention such as visual processes.
use Background service instead

Notification transport controls dont appear to be doing anything

I have created an app that can play audio using a MediaBrowserServiceCompat and a MediaSessionCompat. As per the instructions on the android developers website, I have created a notification in the MediaSessionCompat.Callback().onPlay() method that uses MediaStyle to provide transport controls which are supposed to connect to my media session when provided with the appropriate token. The in app controls for playing and pausing work as expected, even when the app is closed and opened again. The service appears to be running as expected.
The problem however, is that although the notification appears as expected, the included pause button is seemingly unable to do anything. And despite the fact that the android developers example indicates that a cancel button should be present, it is not. Furthermore the example also indicated that the service should be stoppable by swiping the notification away, and yet it does not.
Suffice it to say, nothing in the following code snippet is working correctly. Except that the notification does, in fact, appear.
private NotificationCompat.Builder getMediaNotificationBuilder() {
Intent contentIntent = new Intent(mContext, MainActivity.class);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingContentIntent = PendingIntent.getActivity(mContext, 0, contentIntent, 0);
MediaControllerCompat controller = mMediaSession.getController();
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, "PODCAST");
builder
.setContentTitle("PODCAST")
.setContentText("THIS IS A PLACE HOLDER.")
.setSubText("Still a place holder.")
// Enable launching the player by clicking the notification
.setContentIntent(pendingContentIntent)
// Stop the service when the notification is swiped away
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext, PlaybackStateCompat.ACTION_STOP))
// Make the transport controls visible on the lockscreen
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
// Add an app icon and set its accent color
// Be careful about the color
.setSmallIcon(R.drawable.ic_launcher_background)
.setColor(ContextCompat.getColor(mContext, R.color.colorPrimaryDark))
// Add a pause button
.addAction(new NotificationCompat.Action(
R.drawable.ic_pause, "Pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
PlaybackStateCompat.ACTION_PAUSE)))
// Take advantage of MediaStyle features
.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mMediaSession.getSessionToken())
.setShowActionsInCompactView(0)
// Add a cancel button
.setShowCancelButton(true)
.setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
PlaybackStateCompat.ACTION_STOP)));
return builder;
}
I then go on to pass this notification to
startForground(1, getMediaNotificationBuilder().build())
and then start the service.
I will be happy to share the entire app source code if it is necessary. I am sure that I have missed something very simple here.
As I suspected I was missing something very simple. In order for my MediaBrowserServiceCompat subclass to react to my notification controls, I needed to override onStartCommand from the Service base class and pass the Intent there in to my MediaSessionCompat object. After doing this, the MediaSessionCompat.Callback should handle the command assuming it has been programed to do so. This is what the code for that looks like, inside of my MediaBrowserService class.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(LOG_TAG, "onStartCommand(): received intent " + intent.getAction() + " with flags " + flags + " and startId " + startId);
MediaButtonReceiver.handleIntent(mMediaSession, intent);
return super.onStartCommand(intent, flags, startId);
}
After adding this code you should see the method in logcat as well. Just in case anyone out there is still missing something, you will at least know that the code is responding to your button presses.
EDIT:
As for stopping the Service by swiping the notification, I was misunderstanding the interaction between the notification and the user. The notification CAN be swiped away by the user but only if the media is PAUSED first. This paradigm is further supported by the standard media player app's notification controls. This makes sense as the user might accidentally swipe away the controls while in the middle of listening to something otherwise.
In addition I have decided to include the entire source code for my MediaBrowserServiceCompat class in hopes that this additional information will
provide some context for disscussion
public class MediaPlaybackService extends MediaBrowserServiceCompat {
private static final String LOG_TAG = "MediaPlaybackService";
private static final String MY_MEDIA_ROOT_ID = "media_root_id";
private static final String MY_EMPTY_MEDIA_ROOT_ID = "empty_root_id";
// Volume levels: Normal and Duck
// VOLUME_DUCK is the volume we set the media player to when we lose audio focus, but are allowed to reduce the volume instead of stopping playback.
public static final float VOLUME_DUCK = 0.2f;
public static final float VOLUME_NORMAL = 1.0f;
private MediaSessionCompat mMediaSession;
private MediaPlayer mMediaPlayer;
// Current local media player state
private PlaybackStateCompat.Builder mStateBuilder;
private int mState = PlaybackStateCompat.STATE_NONE;
private final class MediaSessionCallback extends MediaSessionCompat.Callback implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, AudioManager.OnAudioFocusChangeListener{
private Context mContext;
private AudioManager mAudioManager;
// Declare the "SHIT THAT'S LOUD" intent, any broadcast receiver
// that is connected to it will trigger when the headphones come unplugged
private IntentFilter shitThatsLoudIntentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private BroadcastReceiver shitThatsLoudBroadcastReceiver = new BroadcastReceiver() {
// TODO: Put me in a separate class
#Override
public void onReceive(Context context, Intent intent) {
Log.d(LOG_TAG, "SHIT THATS LOUD! The headphones have come unplugged!");
}
};
private MediaSessionCallback(Context context) {
super();
mContext = context;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
initMediaPlayer();
}
private void initMediaPlayer() {
try {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource("https://www.blogtalkradio.com/kylekulinski/2018/10/15/the-kyle-kulinski-show.mp3");
mMediaPlayer.setOnPreparedListener (this);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnErrorListener (this);
mMediaPlayer.prepare();
} catch (IOException e) {
Log.e(LOG_TAG, ".initMediaPlayer(): IOException: "+e.toString());
}
}
private void mediaPlay() {
registerReceiver(shitThatsLoudBroadcastReceiver, shitThatsLoudIntentFilter);
if (mAudioManager.requestAudioFocus(getAudioFocusRequest()) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(LOG_TAG, "Audio focus request granted.");
mState = PlaybackStateCompat.STATE_PLAYING;
mStateBuilder.setActions(PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_STOP);
mStateBuilder.setState(mState, mMediaPlayer.getCurrentPosition(), 1.0f, SystemClock.elapsedRealtime());
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setActive(true);
mMediaPlayer.start();
startService(new Intent(mContext, MediaPlaybackService.class));
startForeground(1, getMediaNotificationBuilder().build());
}
}
private void mediaPause() {
unregisterReceiver(shitThatsLoudBroadcastReceiver);
mState = PlaybackStateCompat.STATE_PAUSED;
mStateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_STOP);
mStateBuilder.setState(mState, mMediaPlayer.getCurrentPosition(), 1.0f, SystemClock.elapsedRealtime());
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaPlayer.pause();
stopForeground(false);
}
private void releaseResources() {
mMediaSession.setActive(false);
mAudioManager.abandonAudioFocusRequest(getAudioFocusRequest());
unregisterReceiver(shitThatsLoudBroadcastReceiver);
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
}
stopSelf();
stopForeground(true);
}
private NotificationCompat.Builder getMediaNotificationBuilder() {
Intent contentIntent = new Intent(mContext, MainActivity.class);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingContentIntent = PendingIntent.getActivity(mContext, 0, contentIntent, 0);
MediaControllerCompat controller = mMediaSession.getController();
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, "PODCAST");
builder
.setContentTitle("PODCAST")
.setContentText("THIS IS A PLACE HOLDER.")
.setSubText("Still a place holder.")
// Enable launching the player by clicking the notification
.setContentIntent(pendingContentIntent)
// Stop the service when the notification is swiped away
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext, PlaybackStateCompat.ACTION_STOP))
// Make the transport controls visible on the lockscreen
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
// Add an app icon and set its accent color
// Be careful about the color
.setSmallIcon(R.drawable.ic_launcher_background)
.setColor(ContextCompat.getColor(mContext, R.color.colorPrimaryDark))
// Add a pause button
.addAction(new NotificationCompat.Action(
R.drawable.ic_pause, "Pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
PlaybackStateCompat.ACTION_PLAY_PAUSE)))
// Take advantage of MediaStyle features
.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mMediaSession.getSessionToken())
.setShowActionsInCompactView(0)
// Add a cancel button
.setShowCancelButton(true)
.setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
PlaybackStateCompat.ACTION_STOP)));
return builder;
}
#Override
public void onPlay() {
super.onPlay();
Log.d(LOG_TAG, "I tried to play music");
mediaPlay();
}
#Override
public void onPause() {
super.onPause();
Log.d(LOG_TAG, "I Tried to pause");
mediaPause();
}
#Override
public void onStop() {
super.onStop();
releaseResources();
}
private AudioFocusRequest getAudioFocusRequest() {
// Request audio focus for playback, this registers the afChangeListener
AudioAttributes attrs = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setOnAudioFocusChangeListener(this)
.setAudioAttributes(attrs)
.build();
return audioFocusRequest;
}
#Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
Log.d(LOG_TAG, "Audio focus has been restored after it was transiently arrested by and intrusive app. We can now start playing audio normally again.");
mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL);
mediaPlay();
break;
case AudioManager.AUDIOFOCUS_LOSS:
Log.d(LOG_TAG, "Audio focus was lost flat out. Save what we were doing so we don't forget about it later.");
mediaPause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
Log.d(LOG_TAG, "Audio focus was lost (Transient) but we might get it back later, still stop and save though.");
mediaPause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
Log.d(LOG_TAG, "Audio focus was lost but was just need to keep it down instead of stopping.");
mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK);
break;
default:
Log.d(LOG_TAG, "Ignoring unsupported audio focus change: "+focusChange);
break;
}
}
#Override
public void onPrepared(MediaPlayer mp) {
Log.d(LOG_TAG, "MediaSessionCallback.onPrepared(): MediaPlayer is prepared!");
// The media player is done preparing. That means we can start playing if we
// have audio focus.
}
#Override
public void onCompletion(MediaPlayer mp) {
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.e(LOG_TAG, "Media player error: what=" + what + ", extra=" + extra);
return false; // true indicates we handled the error
}
}
#Override
public void onCreate() {
super.onCreate();
// Create a MediaSessionCompat
mMediaSession = new MediaSessionCompat(this, LOG_TAG);
// Set the session's token so that client activities can communicate with it.
setSessionToken(mMediaSession.getSessionToken());
// MediaSessionCallback() has methods that handle callbacks from a media controller
mMediaSession.setCallback(new MediaSessionCallback(this));
// Enable callbacks from media buttons and transport controls
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
);
// Set initial PlaybackState with ACTION_PLAY, so that media buttons start the player
mStateBuilder = new PlaybackStateCompat.Builder()
.setActions(
PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE
);
mMediaSession.setPlaybackState(mStateBuilder.build());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(LOG_TAG, "onStartCommand(): received intent " + intent.getAction() + " with flags " + flags + " and startId " + startId);
MediaButtonReceiver.handleIntent(mMediaSession, intent);
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName, int clientUid, #Nullable Bundle rootHints) {
return new BrowserRoot(MY_EMPTY_MEDIA_ROOT_ID, null);
}
#Override
public void onLoadChildren(#NonNull String parentMediaId, #NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
// Browsing not allowed
if (TextUtils.equals(MY_EMPTY_MEDIA_ROOT_ID, parentMediaId)) {
result.sendResult(null);
return;
}
// TODO: If in the future we decide that we do want this class to handle the podcast metadata
// Then we must adapt what ever data podcastFactory produces into a List of MediaBrowserCompat.MediaItem objects
// The constructor of MediaItem requires that a MediaDescription object be passed to it.
// MediaDescription has a builder class which contains methods for setting Title, Artist, Uri, etc...
// MediaDescription.Builder mMediaDescriptionBuilder = new MediaDescription.Builder();
// mMediaDescriptionBuilder.setTitle(String);
// mMediaDescriptionBuilder.setMediaUri(String);
// MediaDescription mMediaDescription = mMediaDescriptionBuilder.build()
// MediaBrowserCompat.MediaItem mMediaItem =
// new MediaBrowserCompat.MediaItem(
// mMediaDescription,
// int flags -> FLAG_BROWSABLE and/or FLAG_PLAYABLE
// );
// add MediaItem to SomeList
// result.sendResult(SomeList);
}

Android Geofencing dosen't trigger IntentService

I'm building a mobile application with which I'll be able to test working of geofencing on Android phones. I have an application that uses GoogleApiClient for FusedLocationApi.requestLocationUpdates. I'm getting all the updates which I show on the map in my MapFragment. I then try to add the geofences with GeofencingClient.
private void addGeofenceList() {
mGeofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent())
.addOnSuccessListener(this, new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.wtf(TAG, "addGeofences() - succesfully added");
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.wtf(TAG, "addGeofences() - failed to add");
}
});
}
This code is executed and I get back that they were succesfully added. But when I'm on the location where the geofence should trigger it dosen't call the IntentService. I also add the IntentService:
private PendingIntent getGeofencePendingIntent() {
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(this, GeofenceService.class);
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
I also create a GeofencingRequest where I set the initial trigger when entered.
private GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.addGeofences(mGeofenceList);
return builder.build();
}
And this is where I create the ArrayList<Geofence> mGeofenceList in which I add all the geofences:
public void createGeofenceList() {
GeofenceList geofences = new GeofenceList();
ArrayList<GeofenceModel> geofenceList = (ArrayList) geofences.returnGeofences();
for (GeofenceModel geofence : geofenceList) {
mGeofenceList.add(new Geofence.Builder()
.setRequestId(geofence.getREQ_ID())
.setCircularRegion(
geofence.getLocation().latitude,
geofence.getLocation().longitude,
geofence.getRadius()
)
.setExpirationDuration(geofence.getExpire())
.setTransitionTypes(geofence.getTransition())
.build());
}
addGeofenceList();
}
My whole projects is on GitHub. All the help will be appreciated!
In your manifest replace your service declaration which is
<service android:name="services.GeofenceService" />
to this
<service android:name="services.GeofenceService"
android:exported="true"/>
Basically you should mark your service as exported so Android OS can invoke your IntentService

IntentService does not get started

I'm making an android application which uses it's own Camera activity. The images are then stored on the local SQLite database by using an IntentService.
Problem now is: the IntentService doesn't get started after the picture is taken. When using this IntentService on other occations it works perfect so I am at a loss on the why it is not working...
Below is the code used in my CameraActivity to take a picture and start the IntentService.
mPicture = getPictureCallback();
mRaw = getRawCallback();
mShutter = getShutterCallback();
mCamera.takePicture(mShutter, mRaw, mPicture);
private PictureCallback getPictureCallback() {
PictureCallback picture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.d(LOG_TAG, "Picture taken, starting service");
Intent databaseIntent = new Intent(MyCameraActivity.this, DBHandlerService.class);
databaseIntent.putExtra("image", data);
databaseIntent.putExtra("action", "save");
databaseIntent.putExtra(AppConstant.RESULT_RECEIVER, mReceiver);
startService(databaseIntent);
mPreview.refreshCamera(mCamera);
}
};
return picture;
}
The onHandleEvent:
#Override
protected void onHandleIntent(Intent intent) {
Log.d(LOG_TAG, "onHandle started");
Database db = getDB();
action = intent.getStringExtra("action");
Bundle resultbundle = new Bundle();
Log.d(LOG_TAG, "onHandleIntent action : " + action);
final ResultReceiver reciever = intent
.getParcelableExtra(AppConstant.RESULT_RECEIVER);
reciever.send(STATUS_RUNNING, Bundle.EMPTY);
//Different actions depending on the "action" string
if(action.equals("xxxxx"){
//perform action
} else {
// inform activity the requested action does not exist. When
// STATUS_ERROR is received they must check if errorcode is not
// null.
resultbundle.putString("errormsg", "No actions undertaken");
resultbundle.putInt("errorcode", 9999);
reciever.send(STATUS_ERROR, resultbundle);
}
}
I also use this IntentService to get all the pictures from the database. Since the DB stays empty no images are returned but every log gets printed.
If i try it to save the image the logs inside the IntentService are nevr printed.
If more code is needed, let me know.
Thanks in advance!
Thomas

Android sticky service is beeing killed loosing all information

I have service which is basically timer. It receives object with location data, start time, user id etc, and every seconds it increments stop value. I'd like to have this service running non-stop until stopped in application. I've read that i need to start this service as sticky, so i did that. But I've noticed that after my main application is beeing killed by system or user, service restarts and lose all information (about current timers running etc - I have there array with list of objects).
Idea of that service was that:
user clicks button in app -> service is starting and counting time changing information in notification bar and sending broadcast to main application -> I wish to continue updating notification bar even if app is killed by user or system.
My class below:
public class TimerService extends Service{
private NotificationCompat.Builder builder;
private static JsonHistoryList activities;
private Intent intent;
private Handler handler;
private Runnable sendUpdateToUi = new Runnable() {
#Override
public void run() {
sendBroadcast();
handler.postDelayed(this, 1000);
}
};
private NotificationManager mNotificationManager;
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(ACTION_ADD)){
JsonHistory item = (JsonHistory) intent.getSerializableExtra(HISTORY_ARG);
new NetworkAsyncTask().execute(ACTION_ADD_ID, item);
}else if(action.equals(ACTION_DELETE)){
int id = intent.getIntExtra(CATEGORY_ID_ARG, -1);
if(id > 0){
new NetworkAsyncTask().execute(ACTION_DELETE_ID, id);
}
}else if(action.equals(ACTION_CLEAR)){
activities.clear();
}
}
};
public TimerService() {
handler = new Handler();
}
private void sendBroadcast() {
Long time = new Date().getTime();
for(JsonHistory item: activities){
ContentValues values = new ContentValues();
values.put(History.C_STOP, time);
String[] selectionArgs = {String.valueOf(item.id)};
getContentResolver().update(History.URI, values, History.C_ID + "=?", selectionArgs);
item.stop = time;
}
intent.putExtra(ACTIVITY_LIST_ARG, activities);
Intent bIntent = new Intent(this, MainActivity.class);
bIntent.putExtra(CATEGORY_ARG, activities);
builder.setContentText(String.format(getString(R.string.notification_bar_message), activities.size()));
Notification barNotif = builder.build();
mNotificationManager.notify(SERVICE_ID, barNotif);
sendBroadcast(intent);
}
#Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_ADD);
filter.addAction(ACTION_DELETE);
filter.addAction(ACTION_CLEAR);
registerReceiver(receiver, filter);
intent = new Intent(ACTION);
handler.removeCallbacks(sendUpdateToUi);
handler.postDelayed(sendUpdateToUi, 1000);
activities = new JsonHistoryList();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if(intent != null && intent.hasExtra(TimerService.ACTIVITY_LIST_ARG)){
ArrayList<JsonHistory> temp = (ArrayList<JsonHistory>) intent.getSerializableExtra(TimerService.ACTIVITY_LIST_ARG);
if(temp != nu;; && temp.size() > 0)activities.addAll(temp);
}
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Intent bIntent = new Intent(this, MainActivity.class);
PendingIntent pbIntent = PendingIntent.getActivity(this, 0, bIntent, 0);
builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(getString(R.string.app_name))
.setContentText(String.format(getString(R.string.notification_bar_message), activities.size()))
.setAutoCancel(true)
.setOngoing(true)
.setContentIntent(pbIntent);
Notification barNotif = builder.build();
this.startForeground(SERVICE_ID, barNotif);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
handler.removeCallbacks(sendUpdateToUi);
stopForeground(true);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private class NetworkAsyncTask extends AsyncTask<Object, Void, String>{
Gson gson;
public NetworkAsyncTask() {
super();
gson = new Gson();
}
#Override
protected String doInBackground(Object... params) {
Integer actionId = (Integer) params[0];
String result = null;
switch (actionId){
case ACTION_ADD_ID:
break;
case ACTION_DELETE_ID:
break;
default:
result = null;
break;
}
return result;
}
}
}
Thanks for any replies
When you stop the service it is true it loses its information if the application is destroyed. That is because that instance of the service is tied to that particular instance of the application.
In my opinion, an easy fix would be to store the information you still need in some type of persistent storage (SQLite, Internal/External Memory, Shared Preferences)
Then, every time you start your service with a new application launch just be sure to reload the information you want from whichever persistent storage method you choose.
Also, assuming you don't want this service to continue if the application is killed by the user, be sure to send an intent to the service to stop it like this:
intent = new Intent(this, BackgroundLocationService.class);
stopService(intent);

Categories