I have to create a service which tracks the GPS location of the user even when the application is killed until when i stop the service. Is it possible?
I have created a service which makes use of LocationListener and also made use of socket.io which communicates with server. But when the application is paused the location updates donot reflect and when i kill the application the socket closes. Please suggest a proper way to implement.
Link to the service i have created
This is my onStartCommand
h = new Handler();
r = new Runnable() {
#Override
public void run() {
startSomeJob();
startForeground(101, updateNotification(0));
}
};
h.post(r);
And here is notification:
private Notification updateNotification(int progress) {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
new Intent(action), 0);
String info = progress + "%";
NotificationManager mNotifyManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) if (mNotifyManager != null) {
createChannel(mNotifyManager);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "101")
.setContentTitle(info)
.setTicker(info)
.setContentText("Uploading")
.setVibrate(new long[] {0L})
.setProgress(100, progress, false)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setOngoing(true);
return builder.build();
}
And in my someJob if there is some update, it call startForeground(101, updateNotification(percentDone));
In your case when location is changed in function onLocationChanged you need to call startForeground with notification.
Related
I have created a foreground service using the following code which is in the override method OnStartCommand inside a service class called DemoIntentService.cs.
base.OnStartCommand(intent,flags,startId);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
Intent notificationIntent = new Intent(this, Java.Lang.Class.FromType(typeof(DemoIntentService)));
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, 0);
Notification.Builder notificationBuilder = new Notification.Builder(this, "Example_Service_Channel")
.SetSmallIcon(Resource.Drawable.AlertLightFrame)
.SetContentTitle(Resources.GetString(Resource.String.DialogAlertTitle))
.SetContentText(Resources.GetString(Resource.String.SelectTextMode))
.SetContentIntent(pendingIntent);
Notification notificationAfterBuild = notificationBuilder.Build();
StartForeground(123, notificationAfterBuild);
InitializeAlarmManager();
setAlarm();
}
return StartCommandResult.RedeliverIntent;
Obviously, the code above is only for Android Oreo 8.0 and above, the service works fine and the notification will not be cleared even though I close the app manually. (That's good, that's what I want !). However, when I use the above code to test on Android Nougat 7.1.1, it would not work.
Firstly, I have researched online they said there is no need to create a notification channel for Android below 8.0, so I remove the "Example_Service_Channel" which is the channelID. The app was deployed successfully, but the notification gone when I kill the app. Second thing, when I removed the channelID, Xamarin throw me a warning said "Notification.Builder.Builder(Context) is obsolete : deprecated" and the line has turn yellow. I ignore the error and deploy the app. The service did run as it is visible in the running service inside the developer options. But when I killed the app, the service and notification gone together. Is there any other way to create a foreground notification service that will never end for Android below 8.0? Thanks for any comment and idea.
i write a simple sample,and it works on Android 7.1. i just delete the Notification Channel from Android 8.0
1.Create a Service MyService.cs :
[Service(Enabled = true)]
public class MyService : Service
{
private Handler handler;
private Action runnable;
private bool isStarted;
private int DELAY_BETWEEN_LOG_MESSAGES = 5000;
private int NOTIFICATION_SERVICE_ID = 1001;
private int NOTIFICATION_AlARM_ID = 1002;
public override void OnCreate()
{
base.OnCreate();
handler = new Handler();
//here is what you want to do always, i just want to push a notification every 5 seconds here
runnable = new Action(() =>
{
if (isStarted)
{
DispatchNotificationThatAlarmIsGenerated("I'm running");
handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
}
});
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
if (isStarted)
{
// service is already started
}
else
{
DispatchNotificationThatServiceIsRunning();
handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
isStarted = true;
}
return StartCommandResult.Sticky;
}
public override void OnTaskRemoved(Intent rootIntent)
{
//base.OnTaskRemoved(rootIntent);
}
public override IBinder OnBind(Intent intent)
{
// Return null because this is a pure started service. A hybrid service would return a binder that would
// allow access to the GetFormattedStamp() method.
return null;
}
public override void OnDestroy()
{
// Stop the handler.
handler.RemoveCallbacks(runnable);
// Remove the notification from the status bar.
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Cancel(NOTIFICATION_SERVICE_ID);
isStarted = false;
base.OnDestroy();
}
//start a foreground notification to keep alive
private void DispatchNotificationThatServiceIsRunning()
{
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.SetDefaults((int)NotificationDefaults.All)
.SetSmallIcon(Resource.Drawable.Icon)
.SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
.SetSound(null)
.SetPriority(NotificationCompat.PriorityDefault)
.SetAutoCancel(false)
.SetContentTitle("Mobile")
.SetContentText("My service started")
.SetOngoing(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.From(this);
StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
}
//every 5 seconds push a notificaition
private void DispatchNotificationThatAlarmIsGenerated(string message)
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
Notification.Builder notificationBuilder = new Notification.Builder(this)
.SetSmallIcon(Resource.Drawable.Icon)
.SetContentTitle("Alarm")
.SetContentText(message)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent);
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(NOTIFICATION_AlARM_ID, notificationBuilder.Build());
}
}
2.in your activity :
protected override void OnResume()
{
base.OnResume();
StartMyRequestService();
}
public void StartMyRequestService()
{
var serviceToStart = new Intent(this, typeof(MyService));
StartService(serviceToStart);
}
try to start the service with
ContextCompat.startForegroundService(context,intent)
build the notification then call
startForeground(1, notification)
in onCreate() or onStartCommand() whatever works for you but after the service started and running don't forget to ask for permission
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
I have used foreground service but when the app is in bg, it shows task Completed which I dont want. How can I remove it? If this line (.addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates),
servicePendingIntent)) is removed, the bg service doesn't work. If this code is used: '.setContentIntent(servicePendingIntent)', when I click in the noti, the app doesn't open, noti closed and service stops. How can I solve it? Thanks in advance
private Notification getNotification() {
Intent intent = new Intent(this, LocationUpdatesService.class);
CharSequence text = Utils.getLocationText(mLocation);
// Extra to help us figure out if we arrived in onStartCommand via the notification or not.
intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true);
// The PendingIntent that leads to a call to onStartCommand() in this service.
PendingIntent servicePendingIntent = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent activityPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LiveTrack.class), 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates),
servicePendingIntent)
.setContentIntent(activityPendingIntent)
// .setContentIntent(servicePendingIntent)
.setContentText("App name")
.setContentTitle(Utils.getLocationTitle(this))
.setOngoing(true)
.setPriority(Notification.PRIORITY_HIGH)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker(text)
.setWhen(System.currentTimeMillis());
// Set the Channel ID for Android O.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID); // Channel ID
}
return builder.build();
}
private void onNewLocation(Location location) {
mLocation = location;
// Notify anyone listening for broadcasts about the new location.
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtra(EXTRA_LOCATION, location);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
// Update notification content if running as a foreground service.
if (serviceIsRunningInForeground(this)) {
mNotificationManager.notify(NOTIFICATION_ID, getNotification());
}
}
public boolean serviceIsRunningInForeground(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
Integer.MAX_VALUE)) {
if (getClass().getName().equals(service.service.getClassName())) {
if (service.foreground) {
return true;
}
}
}
return false;
}
If you will look at official document, it says
For user-initiated work that need to run immediately and must execute
to completion, use a foreground service. Using a foreground service
tells the system that the app is doing something important and it
shouldn’t be killed. Foreground services are visible to users via a
non-dismissible notification in the notification tray.
Even in the services document it says
A foreground service performs some operation that is noticeable to the
user. For example, an audio app would use a foreground service to play
an audio track. Foreground services must display a Notification.
Foreground services continue running even when the user isn't
interacting with the app.
So it seems, there must be non-dismissible notification when you are using foreground services.
I was trying to implement a feature where user can set a reminder that will be delivered with a local push notification.
I am using AlarmManager to throw a broadcast when the time is up.
Then in the broadcast receiver, I am posting a local notification and then starting a foreground service which starts an activity so I can wake the device and turn on the screen.
If I do nothing after posting the notification (simply return and not starting foreground service) I can get the device to show the notification with no problem.
However, if I start the service right after posting the notification, all I get is some vibration but I don't see the notification anywhere.
Even stranger, from the notification manager, it says there is 1 notification from getActiveNotifications() although there is nothing.
Receiver:
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (!action.equals(context.getString(R.string.reminder_action_string))) {
// fail safe check
return;
}
// create the channel for android 8
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
context.getString(R.string.reminder_notification_channel),
NotificationManager.IMPORTANCE_HIGH);
channel.setDescription(context.getString(R.string.reminder_notification_channel_desc));
final AudioAttributes attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), attributes);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ReceiverReminder.CHANNEL_ID)
.setChannelId(ReceiverReminder.CHANNEL_ID)
.setContentTitle(context.getString(R.string.reminder_title))
.setContentText(intent.getStringExtra(ActivityReminderCreate.TEXT_STRING))
.setSmallIcon(R.drawable.ic_mic)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(intent.getStringExtra(ActivityReminderCreate.TEXT_STRING)))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setVisibility(NotificationCompat.VISIBILITY_SECRET)
.setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, ActivityReminderList.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), PendingIntent.FLAG_ONE_SHOT))
.setAutoCancel(true);
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
Notification notification = builder.build();
final int id = mSecureRandom.nextInt();
manager.notify(id, notification);
Intent serviceIntent = new Intent(context, ServiceReminder.class);
serviceIntent.putExtras(intent);
serviceIntent.putExtra(KEY_ID, id);
serviceIntent.putExtra(KEY_NOTIFICATION, notification);
ContextCompat.startForegroundService(context, serviceIntent);
}
IntentService:
#Override
protected void onHandleIntent(#Nullable Intent intent) {
// construct notification object
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// start self in foreground
NotificationManager manager = getSystemService(NotificationManager.class);
Log.e(TAG, "notification count: " + manager.getActiveNotifications().length);
Notification notification = intent.getExtras().getParcelable(ReceiverReminder.KEY_NOTIFICATION);
int id = intent.getExtras().getInt(ReceiverReminder.KEY_ID);
startForeground(id, notification);
Log.e(TAG, "notification count: " + manager.getActiveNotifications().length);
}
// start an activity so device can be waken up
Intent activityIntent = new Intent(ServiceReminder.this, ActivityReminderList.class);
activityIntent.putExtra(ActivityReminderList.KEY_FROM_SERVICE, true);
startActivity(activityIntent);
}
I figured out why...
I used an IntentService which kills itself when its current job is done.
And in the case of a foreground service, the associated notification is dismissed when the service is stopped.
I've switched to using a Service instead, and the notification would stay there.
I am creating an app that has a constant notification that reports stats to the user. Every 10 minute, I update the stats in the app, then update the notification. The problem is, the stats update in the app, but the notification does not update until I open the app. I call the same method to update the notification regardless of if the app is open or if it is called through a service.
Is there something special I have to do because it's running through a service?
Service Code (again, this works fine):
Thread.sleep(((10 /* minutes */) * 60 * 1000));
Handler handler = new Handler(getApplicationContext().getMainLooper());
Runnable runnable = new Runnable() {
#Override
public void run() {
OverviewFragment.refresh(getApplicationContext());
}
};
handler.post(runnable);
System.out.println("Updated values through service.");
Refresh Code (for notification):
builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher_white)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher_old))
.setAutoCancel(false)
.setContent(remoteViews)
.setContentIntent(pendingIntent)
.setPriority(Notification.PRIORITY_MAX);
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
builder.setOngoing(true);
notificationManager.notify(notificationId, builder.build());
Is it possible to make a notification automatically disappear after a period of time?
You can use the AlarmManager. I think is more appropriate and more easier to implement than an Android Service.
With AlarmManager you do not need worry about make something running until the time finish. Android do that for you, and send a brodcast when it happen. Your application must have a Receiver to get the correct intent.
Look theses examples:
Android: How to use AlarmManager
Alarm Manager Example
Now there is an option called .setTimeoutAfter(long durationMs)
https://developer.android.com/reference/android/app/Notification.Builder.html#setTimeoutAfter(long)
Yeah, you can just create a service that runs in the background that'll timeout after five minutes and delete your notification. Whether you "should" actually do that is up for debate. A notification should be there to notify the user... and the user should be able to dismiss it on their own.
From d.android.com:
A Service is an application component that can perform long-running
operations in the background and does not provide a user interface.
Yeah, it is very easy.
Where you get notification there add one handler if notification is not read by user then remove notification.
#Override
public void onMessageReceived(RemoteMessage message) {
sendNotification(message.getData().toString);
}
add notification code
private void sendNotification(String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("TEST NOTIFICATION")
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
int id = 0;
notificationManager.notify(id, notificationBuilder.build());
removeNotification(id);
}
cancel notification code.
private void removeNotification(int id) {
Handler handler = new Handler();
long delayInMilliseconds = 20000;
handler.postDelayed(new Runnable() {
public void run() {
notificationManager.cancel(id);
}
}, delayInMilliseconds);
}
You could also use a classic Java Runnable for a simple small Thread.
Handler h = new Handler();
long delayInMilliseconds = 5000;
h.postDelayed(new Runnable() {
public void run() {
mNotificationManager.cancel(id);
}
}, delayInMilliseconds);
Also look here:
Clearing notification after a few seconds