I have functionality in my app where a user will select the time when he wants to get notifications from the application. In this case, I am taking the input from user in this format 12:00 AM. I wrote a method for creating notifications in my app which is working fine but I want to call that method every day at a user-selected time. Also, if app is completely destroyed not even running in the background will this method be called at the user-selected time daily?
String user_time=tinyDB.getString("app_check_time"); // This is user selected time e.g, 12:00 PM
Calendar calendar = Calendar.getInstance();
Date date1 = null;
try {
date1=new SimpleDateFormat("h:m a").parse(user_time);
} catch (ParseException e) {
e.printStackTrace();
}
calendar.set(Calendar.HOUR_OF_DAY, date1.getHours());
calendar.set(Calendar.MINUTE, date1.getMinutes());
Date time = calendar.getTime();
System.out.println("hourr "+time);
timer = new Timer();
timer.schedule(new createNotification(), time);
You can use WorkManager for this. And yes, it'll run even if your app is completely dead.
From Android Docs:
WorkManager is a library used to enqueue deferrable work that is guaranteed to execute sometime after its Constraints are met.
You can see the guide here https://developer.android.com/topic/libraries/architecture/workmanager
The WorkManager API makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or device restarts.
Hope it helps!
Have you considered using something like Quartz Scheduler? This library has a mechanism for scheduling tasks to run at a set period of time every day using a cron like expression (take a look at CronScheduleBuilder).
Some example code (not tested):
public class GetDatabaseJob implements InterruptableJob
{
public void execute(JobExecutionContext arg0) throws JobExecutionException
{
getFromDatabase();
}
}
public class Example
{
public static void main(String[] args)
{
JobDetails job = JobBuilder.newJob(GetDatabaseJob.class);
// Schedule to run at 5 AM every day
ScheduleBuilder scheduleBuilder =
CronScheduleBuilder.cronSchedule("0 0 5 * * ?");
Trigger trigger = TriggerBuilder.newTrigger().
withSchedule(scheduleBuilder).build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(job, trigger);
scheduler.start();
}
}
There's a bit more work upfront, and you may need to rewrite your job execution code, but it should give you more control over how you want you job to run. Also it would be easier to change the schedule should you need to
Creating a long running service for this task
Intent intent = new Intent(this, ReminderService.class);
startService(intent);
Creating a reminderservice which extends service
public class ReminderService extends Service {
#Override
public void onCreate() {
Calendar calendar = Calendar.getInstance();
calendar.set(calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH),
6,
0,
0);
setAlarm(calendar.getTimeInMillis());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// If we get killed, after returning from here, restart
return START_STICKY;
}
public void setAlarm(long timeInMillis) {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, ReminderReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this,
0, intent, 0);
if (alarmManager != null) {
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, timeInMillis,
2 * 60 * 1000 , pendingIntent); //AlarmManager.INTERVAL_DAY //2 * 60 * 1000 (2 minutes)
}
}
}
creating ReminderReceiver which extends BroadcastReceiver
public class ReminderReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
//do task to show notification
ShowNotification showNotification = new ShowNotification(context);
showNotification.showNotification("Method called");
}
}
create ShowNotification class
public class ShowNotification {
private static final int NOTIFICATION = 0;
private static final String NOTIFICATION_CHANNEL_ID = "100";
private Context context;
private NotificationManager notificationManager;
private ConnectivityManager conManager;
public ShowNotification(Context context) {
this.context = context;
if (notificationManager == null) {
notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
}
if (conManager == null) {
conManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
}
}
/**
* Show a notification while this service is running.
*
* #param key
*/
void showNotification(String key) {
String NOTIFICATION_CHANNEL_NAME = "NOTIFICATION_CHANNEL_NAME";
// In this sample, we'll use the same text for the ticker and the expanded notification
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME, importance);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
notificationManager.createNotificationChannel(notificationChannel);
}
final Intent notificationIntent = new Intent(context, SplashActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
// Set the info for the views that show in the notification panel.
Notification notification = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setWhen(System.currentTimeMillis())
.setContentTitle("Method Called")
.setContentText(key)
.setContentIntent(contentIntent)
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
.build();
// Send the notification.
notificationManager.cancel(NOTIFICATION);
notificationManager.notify(NOTIFICATION, notification);
}
}
In manifest file
<service
android:name=".ReminderService"
android:enabled="true"
android:exported="false" />
<receiver android:name=".ReminderReceiver" />
Related
How can I send scheduled notifications in Android Studio?
I got this task: I want to get notifications at the some chosen time of the day every day. I can easily get them when app is alive, but when its closed notifications don't come up.
I've already tried JobScheduler, AlarmManager and WorkManager and none of these didn't work well.
My project runs at minimum SDK 26 (Android Oreo). Target SDK is 30. Last code version looks like that:
AlertReceiver.java
public class AlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
intent = new Intent(context, NotificationService.class);
context.startService(intent);
}
}
NotificationService.java
public class NotificationService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
showNotifications();
return Service.START_STICKY;
}
private void showNotifications(){
NotificationHelper notificationHelper = new NotificationHelper(getApplicationContext());
NotificationCompat.Builder nb;
nb = notificationHelper.getChannelNotification("Title", "Description");
notificationHelper.getManager().notify(123, nb.build());
}
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
I schedule the alert like so:
...
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, hours);
calendar.set(Calendar.MINUTE, minutes);
AlarmManager alarm_manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlertReceiver.class);
PendingIntent pending_intent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarm_manager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pending_intent);
```
The problem is that from Android 8 calling startService from the background is not allowed:
In this code:
public class AlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
intent = new Intent(context, NotificationService.class);
context.startService(intent);
}
}
You can change from:
context.startService(intent);
To:
ContextCompat.startForegroundService(getApplicationContext(), intent)
And put this in your Manifest:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
This will at least allow your service to run from the background in your current implementation.
You then have 5 seconds to call startForeground() in your Service and post a Notification that lets the user know that your service is running or it will be terminated.
Also for what you are trying to do, I think you will get a better result with:
setExactAndAllowWhileIdle
Or
setAlarmClock
setExact does not work as the name implies. All the AlarmManager documentation needs to be read carefully and I would suggest studying "doze" in detail before attempting any Service implementations that rely on timing.
This is a notification that I used in a project (sorry for Kotlin language):
private fun createNotification() {
val notification = getServiceNotification("")
notificationManager.notify(NOTIFICATION_ID, notification.build())
}
private fun getServiceNotification(contentText: String): Notification.Builder {
var notification = Notification.Builder(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
notification = Notification.Builder(this, NOTIFICATION_SERVICE_CHANNEL_ID)
}
val openAppPendingIntent = PendingIntent.getActivity(
this,
0,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
return notification
.setContentTitle("Notification Title")
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_main)
.setContentIntent(openAppPendingIntent)
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel() {
val notificationChannel = NotificationChannel(
NOTIFICATION_SERVICE_CHANNEL_ID,
NOTIFICATION_SERVICE_CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
).apply {
lightColor = Color.GREEN
lockscreenVisibility = Notification.VISIBILITY_PRIVATE
}
notificationManager.createNotificationChannel(notificationChannel)
}
I want to send offline notifications to users daily.Tried many things like: Alarm manager,Work manager but no luck.The notifications are not triggered or they just trigger at later.
Any way to get the job done?
Alarm function:
public void StartAlarm()
{
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY,23);
calendar.set(Calendar.MINUTE,59);
calendar.set(Calendar.SECOND,0);
AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(getApplicationContext(), AlarmReceiver.class);
//alarmIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Log.d("LOG", String.valueOf(calendar.getTimeInMillis()));
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 156, alarmIntent, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
Work manager code:
public MyWorker(#NonNull Context context, #NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
#NonNull
#Override
public Result doWork() {
StartAlarm();
Log.d("In worker","yes");
return Result.success();
}
Work manager driver:
public void StartPeriodicWorker()
{
final PeriodicWorkRequest periodicWorkRequest = new
PeriodicWorkRequest.Builder(MyWorker.class,24,TimeUnit.HOURS)
.addTag("Birthday")
.build();
WorkManager.getInstance(getApplicationContext()).enqueueUniquePeriodicWork("Birthday Notifier", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);
}
Alarm Receiver:
public class AlarmReceiver extends BroadcastReceiver {
private DatabaseReference myRef;
private ArrayList<String> allPeoples;
private int numberOfPeoples;
private Context ctx;
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"In alarm receiver",Toast.LENGTH_LONG).show();
ctx = context;
initPeoples();
}
public String getCurrentDate() {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat mdformat = new SimpleDateFormat("d MMMM");
String strDate = mdformat.format(calendar.getTime());
return strDate;
}
private void initPeoples() {
FirebaseDatabase database = FirebaseDatabase.getInstance();
myRef = database.getReference("Users");
myRef.keepSynced(true);
myRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
allPeoples = new ArrayList<>();
for(DataSnapshot snapshot : dataSnapshot.getChildren()){
if(snapshot.getKey().equals("Teacher") || snapshot.getKey().equals("Staff")){
for(DataSnapshot faculty : snapshot.child("peoples").getChildren()){
String birthday = (String) faculty.child("DOB").getValue();
if(birthday.equals(getCurrentDate())) {
String member = birthday;
allPeoples.add(member);
}
}
}else{
for(DataSnapshot student : snapshot.child("peoples").getChildren()){
String birthday = (String) student.child("DOB").getValue();
if(birthday.equals(getCurrentDate())) {
String member = birthday;
allPeoples.add(member);
}
}
}
}
numberOfPeoples = allPeoples.size();
ShowNotification();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
public void ShowNotification()
{
String CHANNEL_ID = "Channel_1453";
String CHANNEL_NAME = "Birthday Notification";
NotificationManagerCompat manager = NotificationManagerCompat.from(ctx);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH);
manager.createNotificationChannel(channel);
}
Notification notification = new NotificationCompat.Builder(ctx, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_notify)
.setContentTitle("Birthday Reminder")
.setColor(Color.GREEN)
.setContentText("Click to see who has birthday today!")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(PendingIntent.getActivity(ctx, 0, new Intent(ctx, Birthday.class), PendingIntent.FLAG_UPDATE_CURRENT))
.build();
manager.notify(454, notification);
}
}
I can see one possible issue, and it's possible to do this without the workmanager (assuming the device has a healthy connection at the time the notification runs).
Instead of doing your network in the receiver itself, I suggest starting a service (foreground service if above Android 8.0), and doing your work there. This is because android's time limit for a receiver is much lower than a service/foreground service. So to me, it sounds plausible that your receiver is killed before the network request completes, and so no notification is shown.
You can show the notification in the service, and also schedule the next alarm since setExactAndAllowWhileIdle isn't a repetitive alarm on it's own. So in your receiver, something like:
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, BirthdayNotifyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(service);
} else {
context.startService(service);
}
}
Then a possible service class:
public class BirthdayNotifyService extends IntentService {
public BirthdayNotifyService() {
super("BirthdayNotifyService");
}
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//Build a simple notification to show if a foreground service is neccesary
Notification noti = notiBuilder.build();
startForeground(1, noti);
}
//Do your network request work here. After completed, show your birthday notification and stop the foreground service.
}
}
To stop the service, right AFTER you display your notification for birthdays, use the following:
stopForeground(true);
stopSelf();
UPDATE: I used this method on my phone (Xiaomi Redmi Note 8 plus), it worked a few times but then stopped working. It works perfectly fine on stock android, but not on all devices. Therefore I would still say this is not a reliable solution to send notification on specific times.
It seems like you are doing everything right, and as you said in the comments that it's working on nexus S, I assume that you also declared the receiver in the manifest.
So after week of having the same problem here is the result:
In some devices with custom OS like Xiaomi, when you swipe the app away from the list of recents, the OS considers it as a force stop and therefor all services and other stuff will be killed. Based on Issue Tracker there is no way around it at the moment to solve this issue. They responded that they are working with OEMs to resolve this issue.
But based on my own experience, if you setup your notification using work manager, it will be delayed, but you will eventually receive it (at least most of the time).
But if you want the timing to be exact, there is no way to proceed at the moment.
The only solution at the moment is to give some permission manully at the moment. Please visit dontkillmyapp for more information regarding this manual settings.
*At first integrate *
'$' dependencies { implementation "androidx.core:core:$core_version" }
in your gradle file.
then follow the Code : https://github.com/NafimAhmed/Offline_Notification
What i want: To run a background service that would communicate with our server via RESTFUL webservice after regular interval of 1 minute (Its a strict project requirement. So cannot increase it.)
What i have: I tried various approaches using simple BroadcastReceivers with a simple Service AND WakefulBroadcastReceivers using WakefulIntentService etc..
Main Problem: The main issue is when device screen is ON, everything is working fine on regular/fixed intervals but when the screen goes off OR devices gets locked, then the alarm Manager triggers service with a minimum interval of 5 Minutes.. Thats exactly what i dont want. I want the same 1 minute interval while device is locked/screen off.
Below is my code:
Manifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
<receiver android:name=".MyScheduledReceiver" />
<service android:name=".BackgroundService" />
Activity.java
MyScheduledReceiver.scheduleAlarms(MainActivity.this);
gradle
// Background long running process
compile 'com.commonsware.cwac:wakeful:1.0.+'
repositories {
maven {
url "https://s3.amazonaws.com/repo.commonsware.com"
}
}
BroadcastReceiver
public class MyScheduledReceiver extends WakefulBroadcastReceiver {
private static final int PERIOD = 60 * 1000;
private static final int INITIAL_DELAY = 2000; // 5 seconds
#Override
public void onReceive(Context context, Intent i) {
try {
if (i.getAction() == null) {
WakefulIntentService.sendWakefulWork(context, BackgroundService.class);
} else {
scheduleAlarms(context);
}
} catch (Exception e) {
e.printStackTrace();
}
}
static void scheduleAlarms(Context ctxt) {
AlarmManager mgr = (AlarmManager) ctxt.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(ctxt, MyScheduledReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, i, 0);
mgr.setRepeating(AlarmManager.RTC_WAKEUP, INITIAL_DELAY, PERIOD, pi);
}
}
BackgroundService
public class BackgroundService extends WakefulIntentService {
public BackgroundService() {
super("BackgroundService");
}
#Override
protected void doWakefulWork(Intent intent) {
sendNotification("HELLOO");
stopSelf();
}
#Override
public void onTaskRemoved(Intent rootIntent) {
MyScheduledReceiver.scheduleAlarms(BackgroundService.this);
super.onTaskRemoved(rootIntent);
}
private void sendNotification(String message) {
try {
Intent intent = intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Notification notification;
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("test")
.setContentText(message)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
notification = notificationBuilder.build();
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Currently i am simply showing a notification with a sound in the notification bar from my BackgroundService.
I would really appreciate your help. Thanks !
What you want is not supported on Android 6.0 anyway. Android 6.0 will not issue alarms every minute, to any application, thanks to Doze mode.
The closest thing that will work is:
Have a service that uses a ScheduledExecutorService to get control every minute to do your work
Have that service acquire a WakeLock and keep the CPU on all the time
Have that service use startForeground() and START_STICKY to minimize the amount of time that it is not around and therefore incapable of doing this work
Make sure that application is added to the "ignore battery optimizations" whitelist in Settings
Ignore the cries of anguish from users, complaining that their battery life is atrocious
Try using like this
public static void scheduleAlarms(Context ctxt) {
AlarmManager mgr = (AlarmManager)ctxt.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(ctxt, MyScheduledReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(ctxt,REQUEST_CODE, i, 0);
// We want the alarm to go off 3 seconds from now.
long firstTime = SystemClock.elapsedRealtime();
firstTime += 3 * 1000;//start 3 seconds after first register.
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstTime,
600000, sender);//10min interval
}
Have you tried using a Handler to obtain this goal? AlarmManager will typically work on a battery friendly rhythm when possible. Whereas Handlers are much more exact.
For example, you could run this code in your Service:
// Store this as a member variable
private Handler mHandler = new Handler();
// This can be a member variable or local
private Runnable runnable = new Runnable() {
#Override
public void run() {
// Do stuff
// Tell the Handler to call itself again
mHandler.postDelayed(this, 60000);
}
};
// Put this at the start of `Service`
mHandler.postDelayed(runnable, 60000);
As Commonsware points out, having the Handler declared as it is above would cause the Handler to run on the Main Thread. Here is some documentation on how to get a Handler to run in the background:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
I want to display notification every morning at 9 AM from my app.
So I am using Notification Manager, Alarm Manager, BroadcastReciever and Service to make that possible.
But I have a problem, because the notification shows randomly. When I first start the app and set the time, it works OK, but later the app fires and shows notification at random time.
How I can solve that?
Here is my code:
MainActivity
#Override
protected void onStart() {
super.onStart();
setAlarm();
}
public void setAlarm(){
Calendar calendar = Calendar.getInstance();
Calendar now = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 15);
calendar.set(Calendar.MINUTE, 43);
calendar.set(Calendar.SECOND, 0);
if(calendar.getTime().after(now.getTime())) {
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmIntent = new Intent(MainActivity.this, HoroscopeNotification.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent); }
}
HoroscopNotification (BroadcastReciever)
public class HoroscopeNotification extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent arg1) {
showNotification(context);
}
private void showNotification(Context context) {
Intent service1 = new Intent(context, AlarmService.class);
context.startService(service1);
}
}
AlarmService
public class AlarmService extends Service {
private static final int NOTIFICATION_ID = 1;
private NotificationManager notificationManager;
private PendingIntent pendingIntent;
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#SuppressWarnings("static-access")
#Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
Context context = this.getApplicationContext();
notificationManager = (NotificationManager)context.getSystemService(context.NOTIFICATION_SERVICE);
Intent mIntent = new Intent(this, MainActivity.class);
pendingIntent = PendingIntent.getActivity(context, 0, mIntent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle("Horoskop");
builder.setContentText("Pročitajte današnji horoskop");
builder.setSmallIcon(R.drawable.ic_bik);
builder.setAutoCancel(true);
builder.setContentIntent(pendingIntent);
notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
You'll notice in the Android SDK Reference material for the AlarmManager.setRepeating() states:
Note: as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time as described above. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.
You need to use AlarmManager.set() on pre-APIv19 and AlarmManager.setExact() on APIv19+. When your PendingIntent is fired and you receive your Broadcast in your BroadcastReceiver.onReceive() you can set another exact alarm for the next day.
Alarm Manager Example
I think you should follow above link. From my point of view, your design pattern (setting alarm in Activity class is not a good approach). Instead (like showed in the answer above) you should set your alarm from a service. Also the code for notification goes in BroadcastReceiver class, method OnReceive (In the example it is commented "Put here YOUR code").
Good luck
I want to set an alarm at 9am that will execute only once in a day and with
this alarm i want to execute one BroadcastReciver and through that BroadcastReciver
i want to execute Service.
I am Using the following code but in this code following are problems
1)Alarm is not execute at exact 9am.
2)When it execute it execute more than one times
Please help me to resolve this problems.Any help will be appreciable.
===========================================================
The value of breakfastflag is boolean which i am taking from this activity when user press on the click to customize b
if(breakfastflag){
Intent myIntent = new Intent(SplashScreenActivity.this, MyBreakfastReciver.class);
System.out.println("getting Breakfast Reminder");
pendingIntent = PendingIntent.getBroadcast(SplashScreenActivity.this, 0, myIntent,0);
// Set the alarm to start at approximately 9:00 a.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 9);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
// With setInexactRepeating(), you have to use one of the AlarmManager interval
// constants--in this case, AlarmManager.INTERVAL_DAY.
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
BreakfastReciever.java
==============================
public class MyBreakfastReciver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Intent service1 = new Intent(context, MyBreakfastAlarmService.class);
context.startService(service1);
}
}
MyBreakfastAlarmService.java
===================================
public class MyBreakfastAlarmService extends Service
{
private NotificationManager mManager;
#Override
public IBinder onBind(Intent arg0)
{
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate()
{
// TODO Auto-generated method stub
super.onCreate();
}
#SuppressWarnings("static-access")
#Override
public void onStart(Intent intent, int startId)
{
DatabaseHandler db=new DatabaseHandler(getApplicationContext());
HashMap<String,String> user = new HashMap<String,String>();
String abc="Breakfast";
user= db.getUserCalDetails();
String userid=user.get("userid");
final Calendar c = Calendar.getInstance();
int mYear = c.get(Calendar.YEAR);
int mMonth = c.get(Calendar.MONTH);
int mDay = c.get(Calendar.DAY_OF_MONTH);
String day="";
String month="";
mMonth=mMonth+1;
if(mMonth<=9){
month="0"+mMonth;
}
else{
month=""+month;
}
if(mDay<=9){
day="0"+mDay;
}
else{
day=""+mDay;
}
String year=mYear+"";
String finalDate=year+"-"+month+"-"+day;
int ab=db.getMealDettailsForRemider(userid,abc ,finalDate);
if(ab==0)
{
showNotification(this);
}
}
#SuppressLint("NewApi") private void showNotification(Context context) {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.capture)
.setAutoCancel(true)
.setContentTitle("DietGuru")
.setContentText("You haven't logged your Breakfast for today.")
.setSubText("Would you like to do it now?")
;
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(context, SplashScreenActivity.class);
// The stack builder object will contain an artificial back stack for the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(SplashScreenActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
mBuilder.setContentIntent(resultPendingIntent);
mBuilder.setDefaults(Notification.DEFAULT_ALL);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(1, mBuilder.build());
}
#Override
public void onDestroy()
{
super.onDestroy();
}
}
You should use setRepeating() instead of setInexactRepeating().
EDIT:
I have noticed two more mistakes in your code:
1. You have called mBuilder.setContentIntent(resultPendingIntent) twice. You should call it only once. I think this is why the alarm may be coming more than once.
2. You have written month=""+month;. It should be month=""+mMonth;
Try this. This should work.
EDIT 2:
According to the docs:
as of API 19, all repeating alarms are inexact. If your application needs
precise delivery times then it must use one-time exact alarms, rescheduling
each time as described above. Legacy applications whose targetSdkVersion is
earlier than API 19 will continue to have all of their alarms, including
repeating alarms, treated as exact.
To get the exact time, the only way is to use setExact() which needs to be set each time the alarm rings, because it doesn't repeat.