Im creating an application where I can add appointments to a list.
If an appointment is nearby, I want my app to show a notification in the status bar the day of the appointment at a certain hour.
I used the code from http://developer.android.com/guide/topics/ui/notifiers/notifications.html
to create my notification.
However, the "when" parameter in the script is somewhat confusing because a statusbar notification is always triggered when called.
Notification notification = new Notification(icon, tickerText, when);
What is the best way to schedule such notification?
It seems there is no easy way and I have to create a service that starts a listener Activity with a thread to loop my appointmentdates and show a notification when a date fits the current date?
However, the "when" parameter in the script is somewhat confusing
because a statusbar notification is always triggered when called.
Notification notification = new Notification(icon, tickerText, when);
Exactly - notification is triggered when called. If you set when variable to System.currentTimeMilis() as in the example, it means - show the notification now.
As what triggers your notifications, that is up to you to handle. An Activity doesn't seem like a good choice, but a Service does. Initialize your service on application start (and don't forget to stop it on application exit), and let it do the "listening" and triggering of notifications. It might look as:
public class NotifyService extends Service {
private NotificationManager mNM;
#Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
//do some work, listen for change
if (triggerSatisfied) showNotification();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onDestroy() {
// Cancel the persistent notification.
mNM.cancelAll();
}
#Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
private void showNotification() {
//code for notification goes here
}
public class LocalBinder extends Binder {
NotifyService getService() {
return NotifyService.this;
}
}
Related
I have an IntentService and inside this instant service, in onCreate, I call the method startforeground(). I see then the notification when the intentService is created. However, when the IntentService is destroyed (going to onDestroy), I can see the notification for few seconds after that the service is destroyed. Why is that?
This is the code of the IntentService:
public class USFIntentService extends IntentService {
private static final String TAG = "USFIntentService";
private static final int USF_NOTIFICATION_ID = 262276;
private static final String USF_NOTIFICATION_CHANNEL_ID = "USF_NOTIFICATION_CHANNEL";
public USFIntentService() {
super("USFIntentService");
}
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"in onCreate");
startUsfForegroundService();
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG,"in onDestroy");
}
private void startUsfForegroundService() {
// Define notification channel
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel =
new NotificationChannel(USF_NOTIFICATION_CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
// Build notification to be used for the foreground service.
Notification notification =
new Notification.Builder(this, USF_NOTIFICATION_CHANNEL_ID)
.setContentTitle(getText(R.string.notification_title))
.setContentText(getText(R.string.notification_message))
.setSmallIcon(R.drawable.usf_notification_icon)
.build();
// Set the service as a foreground service.
startForeground(USF_NOTIFICATION_ID, notification);
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i(TAG, "onHandleIntent");
if (intent != null) {
doStuff();
}
Log.i(TAG,"End of onHandleIntent");
}
}
I call this service like this:
Intent startServiceIntent = new Intent(intent);
startServiceIntent.setComponent(new ComponentName(context, USFIntentService.class));
context.startForegroundService(startServiceIntent);
Try to call Service#stopForeground after your job is done to remove it
You can call stopForeground(true) when you finish doing the stuff. So that your service gets immediately removed from foreground state and the parameter true ensures that the notification will be removed.
If STOP_FOREGROUND_REMOVE is supplied, the service's associated notification will be cancelled immediately.
If STOP_FOREGROUND_DETACH is supplied, the service's association with the notification will be severed. If the notification had not yet been shown, due to foreground-service notification deferral policy, it is immediately posted when stopForeground(STOP_FOREGROUND_DETACH) is called. In all cases, the notification remains shown even after this service is stopped fully and destroyed.
stopForeground(STOP_FOREGROUND_REMOVE) // remove with notification
stopForeground(STOP_FOREGROUND_DETACH) // remove only intent and not notification
I'm trying to create a Service to checking data and push notification. I have a problem with call service again after stop it. It like 2 instance or more of the service is running(// my code here running twice and call notification twice after stop and start, I comment it cuz it is a long code xD). How can I prevent it?
From some answer i found, i try to put my thread in onCreate() but no effect.
Here is my code:
public class CheckDataAndPushNotificationService extends Service {
public CheckDataAndPushNotificationService() {
}
private String token;
ArrayList<UsefulRequestData> oldData;
ArrayList<UsefulRequestData> newData;
LocalBroadcastManager localBroadcastManager;
ApiUtil apiUtil;
String notificationNewRequest = "Có yêu cầu mới.";
String notificationCancelRequest = "Có yêu cầu đã bị hủy.";
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
newData = new ArrayList<>();
localBroadcastManager = LocalBroadcastManager.getInstance(this);
apiUtil = new ApiUtil(this);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
// my code here
}, 0, 5000);
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
public void createNotification(String message) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("RequestNotification", "RequestNotification", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("Use to notify if you have new or cancel request.");
notificationManager.createNotificationChannel(channel);
}
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext(), "RequestNotification")
.setSmallIcon(R.mipmap.ic_app_icon) // notification icon
.setContentTitle("XCaller") // title for notification
.setContentText(message)// message for notification
.setAutoCancel(true)// clear notification after click
.setSound(defaultSound);
Intent intent = new Intent(getApplicationContext(), SplashActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pi);
notificationManager.notify(0, mBuilder.build());
}
}
You need to use IntentService. It is perfect for your needs. It automatically creates separated thread for the work that you send to it.
From Documentation:
IntentService is a base class for Services that handle asynchronous
requests (expressed as Intents) on demand. Clients send requests
through Context.startService(Intent) calls; the service is started as
needed, handles each Intent in turn using a worker thread, and stops
itself when it runs out of work.
This "work queue processor" pattern is commonly used to offload tasks
from an application's main thread. The IntentService class exists to
simplify this pattern and take care of the mechanics. To use it,
extend IntentService and implement
onHandleIntent(android.content.Intent). IntentService will receive the
Intents, launch a worker thread, and stop the service as appropriate.
All requests are handled on a single worker thread -- they may take as
long as necessary (and will not block the application's main loop),
but only one request will be processed at a time.
I solved it by override method onDestroy() and cancel the timer. When you call stopService(), onDestroy() will run.
#Override
public void onDestroy() {
timer.cancel();
super.onDestroy();
}
I have an app that has two services.
One is for showing UI for floating (overlay) on other apps using WindowManager. The other is for Location Tracking using GooglePlayAPI. My app always runs these services.
I want these services not to be killed by the OS. So I call Service.startForeground(). However there are two notifications in the notification drawer.
Is there a way to use a single notification for both services?
Yes, it is possible.
If we take a look at Service.startForeground() signature, it accept both notification id & the notification itself (see documentation). Hence, if we want to have an only single notification for more than one foreground services, these services must share the same notification & notification id.
We can use the singleton pattern to get the same notification & notification id. Here is the example implementation:
NotificationCreator.java
public class NotificationCreator {
private static final int NOTIFICATION_ID = 1094;
private static final String CHANNEL_ID = "Foreground Service Channel";
private static Notification notification;
public static Notification getNotification(Context context) {
if(notification == null) {
notification = new NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Try Foreground Service")
.setContentText("Yuhu..., I'm trying foreground service")
.setSmallIcon(R.mipmap.ic_launcher)
.build();
}
return notification;
}
public static int getNotificationId() {
return NOTIFICATION_ID;
}
}
Thus, we can use this class in our foreground services. For example, we have MyFirstService.java & MySecondService.java:
MyFirstService.java
public class MyFirstService extends Service {
#Override
public void onCreate() {
super.onCreate();
startForeground(NotificationCreator.getNotificationId(),
NotificationCreator.getNotification(this));
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
MySecondService.java
public class MySecondService extends Service {
#Override
public void onCreate() {
super.onCreate();
startForeground(NotificationCreator.getNotificationId(),
NotificationCreator.getNotification(this));
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Just try to run these services. Voila! You have a single notification for multiple foreground services ;)!
I am trying to put a notification in the status bar when a service starts and keep it there until I stop the service but is disappears after a few seconds(about 10). Any suggestions as to what I am missing? This worked before I tried to re write using notification.builder for compatibility with api 15. The log entry shows onDestroy is not called until I stop the service so it is still running.
public class MyService extends Service {
private NotificationManager mNM;
private int NOTIFICATION = R.string.service_started;
public void onCreate() {
super.onCreate();
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
showNotification();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("MyService", "Service Started");
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mNM.cancel(NOTIFICATION);
Log.e("MyService", "Service Ended");
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
private void showNotification() {
Notification.Builder builder = new Notification.Builder(getApplicationContext());
builder.setAutoCancel(false)
.setOngoing(true)
.setSmallIcon(R.drawable.myicon)
.setTicker(getText(R.string.service_label))
.setWhen(System.currentTimeMillis())
.setContentTitle(getText(R.string.service_started))
.setContentText(getText(R.string.service_label));
Notification notification = builder.getNotification();
mNM.notify(NOTIFICATION, notification);
}
I had the same problem with an ongoing notification disappearing in ICS on a new phone. The app and notification had worked perfectly in every version of Android I had tested it on previously, and it even works on an ICS emulator. Needless to say this has been driving me crazy for a couple months now, but I finally found the answer.
http://code.google.com/p/android/issues/detail?id=21635
I am using a BroadcastReceiver to monitor incoming calls on the handset and I programmatically enable the receiver when a button is toggled in addition to setting the notification. So I wrote a small test app with the same BroadcastReceiver hooked up and was able to reproduced the problem. I commented out the setComponentEnabledSetting call and the notification no longer disappears.
I'm building an app that will trigger notifications at specific time-intervals during the users waking hours.
I have an alarmManager running inside of a service. The service is explicitly started via button click on the main activity and has the alarmManager executing notifications during specific time invervals. How would I go about stopping the notifications during certain hours of the day? I do not want these notification to be fired, for instance, while the user is sleeping.
My code that is currently firing notifications at user-set intervals is below (imports removed....this is long enough already):
public class FartSmackinChunks extends Service {
public Notification scheduleNotification;
public AlarmManager alarmScheduleManager;
public PendingIntent alarmScheduleIntent;
private Boolean autoUpdateBoolean = true;
private int intervalsGoneByInt = 0;
private Notification notification;
public static final int NOTIFICATION_ID = 1;
#Override
public void onCreate() {
// TODO: Actions to perform when service is created.
int icon = R.drawable.icon;
String tickerText = "INTERVAL FIRED";
long when = System.currentTimeMillis();
scheduleNotification = new Notification(icon, tickerText, when);
alarmScheduleManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
String ALARM_ACTION;
ALARM_ACTION = ScheduleAlarmReceiver.ACTION_REFRESH_SCHEDULE_ALARM;
Intent intentToFire = new Intent(ALARM_ACTION);
alarmScheduleIntent = PendingIntent.getBroadcast(this, 0, intentToFire,
0);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
SharedPreferences mySharedPreferences =
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
boolean autoUpdateBoolean =
mySharedPreferences.getBoolean("storedAutoUpdateBoolean", false);
String updateFreq =
mySharedPreferences.getString("storedInitialAverageTimeInterval", "00:00:00");
SimpleDateFormat dfInterval = new SimpleDateFormat("hh:mm:ss");
Date intervalTimeAsDateObject = null;
long updateFreqMilliLong;
try {
intervalTimeAsDateObject = dfInterval.parse(updateFreq);
} catch (ParseException e) {
e.printStackTrace();
}
updateFreqMilliLong = intervalTimeAsDateObject.getTime() - 18000000;
if (autoUpdateBoolean) {
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
long timetoRefresh = SystemClock.elapsedRealtime() +
updateFreqMilliLong;
alarmScheduleManager.setInexactRepeating(alarmType,
timetoRefresh, updateFreqMilliLong, alarmScheduleIntent);
notifications();
} else alarmScheduleManager.cancel(alarmScheduleIntent);
return Service.START_NOT_STICKY;
};
private void notifications() {
**notification stuff in here***
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Replace with service binding implementation.
return null;
}
#Override
public void onDestroy() {
this.alarmScheduleManager.cancel(alarmScheduleIntent);
}
}
.....and my broadcast receiver implementation here:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class ScheduleAlarmReceiver extends BroadcastReceiver {
public static final String ACTION_REFRESH_SCHEDULE_ALARM
= "com.application.ACTION_REFRESH_SCHEDULE_ALARM";
#Override
public void onReceive(Context context, Intent intent) {
Intent startIntent = new Intent(context, SmokerReducerService.class);
context.startService(startIntent);
}
}
I'm having a little difficulty wrapping my brain around how this should be implemented.
I was thinking to rework this code so that the alarmManager is fired at waketime and stopped at sleepTime, all while inside the service is a Timer that fires the notification method at specific intervals? Is there a better way to go about doing this?
Any input would be appreciated. I've been trying to work this out in my head for days now.
Thanks
EDIT:
#anyone who comes across this intending to use a Timer for daily notifications:
A timer which runs inside of a service will be paused by the runtime when the device is put to sleep (ie...the user puts the phone in standby). Therefor, using a Timer to fire notifications at specific time intervals won't work correctly within a service because when Android pauses the service, it also pauses the timer, which throws off the interval.
The correct way to do this is to use AlarmManager with an array of pending intents to set alarms at specific times during the day. This ensures that even if the phone is put in standby, the notifications (or whatever you want to happen at that time) will still be executed.
I was thinking to rework this code so that the alarmManager is fired at waketime and stopped at sleepTime, all while inside the service is a Timer that fires the notification method at specific intervals? Is there a better way to go about doing this?
To my mind, forget thinking of a 'better' way, it seems the only way. Using a timer to control (enable/disable) another timer isn't so strange and makes complete sense to me.