I'm making an app to remind the user of something. I want to show a notification at some time in the future. I've written the code below, following some tutorials, but it doesn't seem to work. At the time I expect the notification, it doesn't show up.
I'm using a BroadcastReceiver and the AlarmManager to make a notification at the desired time. Here's my (simplified) code.
Code to set the time:
try {
Date date = format.parse(timeInput);//This part works
long time = date.getTime();//Get the time in milliseconds
Intent i = new Intent(getBaseContext(), AlarmReceiver.class);
PendingIntent alarmSender = PendingIntent.getBroadcast(getBaseContext(), 0, i, 0);
AlarmManager am = (AlarmManager) getBaseContext().getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, time, alarmSender);
Toast.makeText(getBaseContext(), "Keep the app running to receive a reminder notification", Toast.LENGTH_LONG).show();
super.onBackPressed();
}catch(Exception e){
Toast.makeText(getBaseContext(), "Parsing error. Format:\ndd/MM/yyyy and HH:mm", Toast.LENGTH_SHORT).show();
}
The AlarmReceiver.onReceive() method:
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, MenuActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, i, 0);
NotificationCompat.Builder nBulder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.notify_icon)
.setContentTitle("title")
.setContentText("text")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat nManager = NotificationManagerCompat.from(context);
nManager.notify(0, nBulder.build());
}
Everything is properly declared in the manifest file.
<receiver
android:name=".AlarmReceiver"
android:enabled="true"
android:exported="true"></receiver>
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
throw new UnsupportedOperationException("Not yet implemented");
}
}
Minor Changes:
try {
long time = System.currentTimeMillis();
Intent i = new Intent(getApplicationContext(), AlarmReceiver.class);
PendingIntent alarmSender = PendingIntent.getBroadcast(getApplicationContext(), 0, i, 0);
AlarmManager am = (AlarmManager) getApplication().getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, time, alarmSender);
} catch(Exception e){
Toast.makeText(getBaseContext(), "Parsing error. Format:\ndd/MM/yyyy and HH:mm", Toast.LENGTH_SHORT).show();
}
Difference between getContext() , getApplicationContext() , getBaseContext() and "this"
I've found another way to do it. Instead of using a BroadcastListener and the AlarmManager, I'm using a new Thread. It waits until System.currentTimeMillis() == time and runs a runnable on the UI thread using runOnUIThread(). In that runnable, a notification is made.
I don't know if this is a good/efficient solution, but it does the job fine.
Related
I have RecyclerView that contains notes, when I add new note I want to add reminder for that particular note but when the notification appears and I click on it I want to navigate to that activity and open that note. Notification works properly but what I want is, when I clicked on the notification open that note I set reminder on it.
AlarmReceiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, ChecklistChildActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, i, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "alarmChannel")
.setContentTitle("title")
.setContentText("text")
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setSmallIcon(R.drawable.alarm)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent);
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(context);
managerCompat.notify(123, builder.build());
}
}
setAlarm method:
private void setAlarm() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR, hour);
calendar.set(Calendar.MINUTE, minute);
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
Toast.makeText(this, "Alarm is set", Toast.LENGTH_SHORT).show();
}
I have an o'clock interface in RecyclerAdapter that I implemented in MainActivity:
#Override
public void onNoteClicked(int position, View itemView) {
Intent intent = new Intent(this, NoteDetail.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("id", noteAdapter.itemList.get(position).getId());
intent.putExtra("title", noteAdapter.itemList.get(position).getTitle());
intent.putExtra("content", noteAdapter.itemList.get(position).getContent());
intent.putExtra("date", noteAdapter.itemList.get(position).getDate());
intent.putExtra("backgroundColor", noteAdapter.itemList.get(position).getBackgroundColor());
startActivity(intent);
}
When I clicked on the notification this method somehow should be called. When I add the note I don't know which position it goes in order to pass that position with the intent, though if I know the position, the position may change when I add or remove another note.
Great, I have an idea, I assume your activity gets id and other data from intent. so we have to put data to alarm intent and read them from AlarmReceiver and put them again into the Activity Intent.
private void setAlarm() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR, hour);
calendar.set(Calendar.MINUTE, minute);
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
intent.putExtra("id", "myId");
intent.putExtra("title", "my title");
pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
Toast.makeText(this, "Alarm is set", Toast.LENGTH_SHORT).show();
}
and get that from the Receiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, ChecklistChildActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.putExtra("id", intent.getStringExtra("id"));
i.putExtra("title", intent.getStringExtra("title"));
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, i, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "alarmChannel")
.setContentTitle("title")
.setContentText("text")
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setSmallIcon(R.drawable.alarm)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent);
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(context);
managerCompat.notify(123, builder.build());
}
that is a simple way but I suggest using the database in your activity and getting the note details in the onCreate method with id, so you need just add id in your intents and add get query in NoteDetail.java class.
This application allows me to start a service to remind me drinking. If i click the notification i want to see if the service is running or not, but i don't get any output with the Intent.putExtra method.
My MainActivity class:
Boolean isServiceRunning = false;
AlarmManager alarmManager;
PendingIntent pendingIntent;
TextView mainText;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainText = findViewById(R.id.mainTextView);
Bundle extras = getIntent().getExtras();
if (extras == null) {
mainText.setText("Service is paused");
} else {
mainText.setText("Service is running");
}
}
public void sendNotification(View view) {
if (!isServiceRunning) {
isServiceRunning = true;
Toast.makeText(this, "Reminder service started.", Toast.LENGTH_SHORT).show();
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
mainText.setText("Service is running.");
Intent i = new Intent(this, reciever.class);
i.putExtra("isServiceRunning", isServiceRunning.booleanValue());
pendingIntent = PendingIntent.getBroadcast(this, 100, i, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * 10, pendingIntent);
} else {
isServiceRunning = false;
Toast.makeText(this, "Reminder service ended.", Toast.LENGTH_SHORT).show();
mainText.setText("Service paused.");
alarmManager.cancel(pendingIntent);
}
}
I think its possible to solve this problem with sharedpreferences, but this shouldn't be a good resolution.
EDIT:
My broadcast reciever class:
public class reciever BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent notificationIntent = new Intent(context, drinkReminder.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(drinkReminder.class);
stackBuilder.addNextIntent(notificationIntent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(100, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
Notification notification = new NotificationCompat.Builder(context, CHANNEL_1_ID)
.setContentTitle("Title")
.setContentText("description")
.setSmallIcon(R.drawable.icon_waterdrop)
.setTimeoutAfter(30000)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build();
notificationManager.notify(0, notification);
}
}
You are adding the "extra" to the Intent that you pass to AlarmManager. This "extra" will be in the Intent that gets delivered to your BroadcastReceiver in onReceive(), but you don't do anything with it there.
When you click on the Notification, it will launch drinkReminder, which I hope is an Activity.
You also said in a comment:
Could you tell me please one more thing. Sometimes if i press the
button to start the service, a notification appers after about 10
seconds, after this it shows all 10 minutes like it should. How can i
fix this?
Here is your code for setting the alarm:
Calendar calendar = Calendar.getInstance();
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), 1000 * 60 * 10, pendingIntent);
When you call setRepeating(), the 2nd parameter specifies the first time the alarm should go off, and the 3rd parameter specifies the repeat interval. You've told alarm manager that you want the first alarm to trigger immediately and then every 10 minutes after that.
If you want the first alarm to trigger 10 minutes after you press the button, you need to adjust the second parameter so it represents a time that is 10 minutes in the future, like this:
Calendar calendar = Calendar.getInstance();
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis() + (1000 * 60 * 10), 1000 * 60 * 10, pendingIntent);
Instead of the code in onCreate() and putExtra() ,you can create two pending intents in each case and each intent uses a different broadcast receiver. In that broadcast receiver you can print the separate toast messages.
MyBroadcastRecieverStart.java
public class MyBroadcastReceiverStart extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(this, "Reminder service started.", Toast.LENGTH_SHORT).show();
}
}
MyBroadcastReceiverStop.java
public class MyBroadcastReceiverStop extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(this, "Reminder service ended.", Toast.LENGTH_SHORT).show();
}
}
SendNotification method -
public void sendNotification(View view) {
if (!isServiceRunning) {
isServiceRunning = true;
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
mainText.setText("Service is running.");
Intent i = new Intent(this, MyBroadcastReceiverStart.class);
pendingIntent = PendingIntent.getBroadcast(this, 100, i, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * 10, pendingIntent);
} else {
isServiceRunning = false;
mainText.setText("Service paused.");
Intent i = new Intent(this, MyBroadcastReceiverStop.class);
pendingIntent = PendingIntent.getBroadcast(this, 100, i, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * 10, pendingIntent);
alarmManager.cancel(pendingIntent);
}
}
If you write alarmManager.cancel(pendingIntent) in else block , it is cancelling an intent which does not exist as you have created the intent in if block and it does not get created if else gets executed , so I have created the intent in else block also.
This question has been asked multiple times. But most of the answers provided are either not working, or a solution specific only to a certain situation. Well I am having difficulty esp on testing my app. I have a code which sets off the alarm like this:
public void setAlarm(Context context)
{
if((mNote == null) && (!mNote.getRemindEnabled() || mNote.getRemindOnDate() == null))
return;
Intent alarmIntent = new Intent(context, NoteAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
Calendar calendar = mNote.getRemindCalendar();
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);
Toast.makeText(context, "NoteAlarm.setAlarm() : Note " + mNote.getShortNote() + " has been set" , Toast.LENGTH_LONG).show();
Log.i(NoteApplication.TAG, "NoteAlarm.setAlarm() : Note " + mNote.getShortNote() + " has been set");
}
AlarmReceiver class receives the said alarm using this code:
#Override
public void onReceive(Context context, Intent intent)
{
mNoteDatabaseHelper = new NoteDatabaseHelper(context);
NotificationCompat.Builder mBuilder = new
NotificationCompat.Builder(context)
.setContentTitle("My Notification")
.setContentText("Hello World!");
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1, mBuilder.build());
Log.i(NoteApplication.TAG, "Alarm has been called!");
}
In order for me to check this, I am setting it fire after one minute. So I have a ui which sets the alarm 1 minute after the current time.
But this is not working at all. I have declared this on my Application manifest as well:
<receiver
android:name="com.neonwarge.android.note.receivers.NoteAlarmReceiver">
</receiver>
I wonder what I still missed? Also, according to the documentation, if I set the alarm at datetime less than the current datetime the alarm immediately fires. But nothing still.
Here are the relative questions I visited but none of it work:
AlarmManager not working
BroadcastReceiver from AlarmManager not getting called
Any ideas?
I'm trying to broadcast after 20 seconds and receive the broadcast with an extended receiver ExtendedReceiver.
Here is my function that creates an alarm and sets the PendingIntent to go off after 20 seconds:
public void alert() {
GregorianCalendar cal = new GregorianCalendar();
cal.add(Calendar.SECOND, 20);
Intent i = new Intent(this, ExtendedReceiver.class);
int _uid = (int) System.currentTimeMillis();
PendingIntent pi = PendingIntent.getBroadcast(this, _uid, i, PendingIntent.FLAG_ONE_SHOT);
AlarmManager am = (AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pi);
Log.i("Title", "Alarm has been set");
}
Here is the ExtendedReceiver class:
public class ExtendedReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("Title", "Broadcast received");
}
}
I get my first log message: Alarm has been set but I don't get my second log message. I'm unsure where the problem lies (first day on Android)
If i may this is what i always do.
change your
PendingIntent pi = PendingIntent.getBroadcast(this, _uid, i, PendingIntent.FLAG_ONE_SHOT);
to
PendingIntent pi = PendingIntent.getBroadcast(this, _uid, new Intent(EXTENDED_RECEIVER_ACTION), PendingIntent.FLAG_ONE_SHOT);
and important: Register your ExtendedReceiver like this:
...
registerReceiver(new ExtendedReceiver() , new IntentFilter(EXTENDED_RECEIVER_ACTION));
PS: EXTENDED_RECEIVER_ACTION is a String and dont forget to unregister your receiver.
further docs here
hope it helps :)
I'm working with 2 separate alarms. I have a method in my class which creates 2 different pending intents to start 2 alarms. This method calls the broadcast method when complete however depending on which intent it receives i need to perform different actions
thanks in advance,
Andy
prompt class
public void setSleepPrompts(Context context){
try{
Intent intent = new Intent(context, SleepPromptReceiver.class );
PendingIntent firstSender = PendingIntent.getBroadcast(context, 1, intent, 0);
PendingIntent secondSender = PendingIntent.getBroadcast(context, 2, intent, 0);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, (this.getBedTimeEpoch() - this.firstPromptOffset), firstSender);
am.set(AlarmManager.RTC_WAKEUP, (this.getBedTimeEpoch() - this.secondPromptOffset), secondSender);
} catch (Exception e){
Log.i(TAG, e.toString());
}
}
Broadcast receiver class
#Override
public void onReceive(Context context, Intent intent) {
try{
if(intent.GET_THE_INTENT_ID?)
Toast.makeText(context, "kapow chow", Toast.LENGTH_SHORT).show();
} catch (Exception e){
Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
}
}
Use intent.putExtra("id", "intent x") when you create the intent, and then
if(intent.getStringExtra("id").equals("intent x"))
Toast.makeText(context, "kapow chow", Toast.LENGTH_SHORT).show();