my note app should remember my users with a notification at their note at a specific date and time the user had set. My Problem is that I don't get the notification at the set time.
This is my alarmManager.class (Here I create my Alarms)
public class alarmManager {
public static void createAlarm(long timeInMillis, int requestCode, Intent i) {
PendingIntent pi = PendingIntent.getService(MainActivity.context, requestCode, i, PendingIntent.FLAG_UPDATE_CURRENT);
getAlarmManager().set(AlarmManager.RTC_WAKEUP, timeInMillis, pi);
}
public static void deleteAlarm(Intent i, int requestCode) {
if (doesPendingIntentExist(MainActivity.context, i, requestCode)) {
PendingIntent pi = PendingIntent.getService(MainActivity.context, requestCode, i, PendingIntent.FLAG_NO_CREATE);
pi.cancel();
getAlarmManager().cancel(pi);
}
}
private static AlarmManager getAlarmManager() {
return (AlarmManager) MainActivity.context.getSystemService(Context.ALARM_SERVICE);
}
private static boolean doesPendingIntentExist(Context context, Intent i, int requestCode) {
PendingIntent pi = PendingIntent.getService(context, requestCode, i, PendingIntent.FLAG_NO_CREATE);
return pi != null;
}
}
When the user creates a new note this lines get fired (They send everything is needed to my alarmManager.class)
Intent i = new Intent(MainActivity.context, NotificationService.class);
i.putExtra(NotificationService.i_UID, id);
i.putExtra(NotificationService.i_TEXT, title);
i.putExtra(NotificationService.i_CONTENT, content);
i.putExtra(NotificationService.i_REMINDEDATE, mUserReminderDate);
i.putExtra(NotificationService.i_REPEAT, repeatAlarmSelectedOption);
alarmManager.createAlarm(mUserReminderDate.getTime(), id.hashCode(), i);
When everything works the NotificationService.class should get called at the chosen time. (Here the notification should show to the user)
public class NotificationService extends IntentService {
public static final String i_UID = "uuid";
public static final String i_TEXT = "title";
public static final String i_CONTENT = "content";
public static final String i_REMINDEDATE = "remindeDate";
public static final String i_REPEAT = "repeatAlarm";
private String mTodoText, mTodoContent, mTodoUUID;
private Date mTodoDate;
private int mTodoRepeat;
private Intent processIntent;
public NotificationService() {
super("NotificationService");
}
#Override
protected void onHandleIntent(Intent intent) {
this.processIntent = intent;
mTodoText = intent.getStringExtra(i_TEXT);
mTodoContent = intent.getStringExtra(i_CONTENT);
mTodoUUID = intent.getStringExtra(i_UID);
mTodoDate = (Date) intent.getSerializableExtra(i_REMINDEDATE);
mTodoRepeat = intent.getIntExtra(i_REPEAT, 0);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification = new Notification.Builder(this)
.setContentTitle(mTodoText)
.setContentText(mTodoContent)
.setSmallIcon(R.drawable.app_logo_transparent_bg)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_SOUND)
.build();
manager.notify(100, notification);
}
Unfortunately it doesn't work. No errors. No notification. Nothing.
I hope someone can help me.
In my opinion the problem is when you create a PendingIntent:
PendingIntent pi = PendingIntent.getService(MainActivity.context, requestCode, i, PendingIntent.FLAG_UPDATE_CURRENT);
To be absolutely sure, schedule an alarm, then run from adb following command:
adb shell dumpsys alarm and search for your PendingIntent. If you find it, schedule one more time alarm and run command one more time. Look again for your pending intent and make sure it is a new one and try to search for this previous scheduled. If it is problem with PendingIntent you will find only the newest PendingIntent.
Solution:
Since AlarmManger while comparing PendingIntents doesn't compare extra data passed it, but compares request code, data, action and categories you need to change one of them every time you schedule a new alarm. My suggestion is to pass uri from database to every new scheduled PendingIntent. That will make each of them uniqal so the older one won't be cancelled when a new scheduled. To sum up, to argument i in your code call method setData() and pass uri as an argument. You can do it in a method or from a class where method is being called. It may look like that:
public static void createAlarm(long timeInMillis, int requestCode, Intent i, Uri uri) {
i.setData(uri)
PendingIntent pi = PendingIntent.getService(MainActivity.context, requestCode, i, PendingIntent.FLAG_UPDATE_CURRENT);
getAlarmManager().set(AlarmManager.RTC_WAKEUP, timeInMillis, pi);
}
Related
I have an app that schedules a bunch of notifications (user has to answer questionnaires) locally using AlarmManager. The notification should show at certain points in the future.
I schedule the notifications like this:
private void scheduleNotification(Notification notification, int delay, int scheduleId, int notificationId) {
Intent notificationIntent = new Intent(context, NotificationPublisher.class);
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID, notificationId);
notificationIntent.putExtra(NotificationPublisher.INTENT, notification);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, scheduleId, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, delay);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
The intent is received by a BroadcastReceiver that calls notify on the notification attached to the intent.
public class NotificationPublisher extends BroadcastReceiver {
public static String NOTIFICATION_ID = "notification-id";
public static String INTENT = "notification";
#Override
public void onReceive(Context context, Intent intent) {
if (intent.hasExtra(INTENT)) {
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
Notification notification = intent.getParcelableExtra(INTENT);
int id = intent.getIntExtra(NOTIFICATION_ID, 0);
notificationManager.notify(id, notification);
}
}
}
This works fine so far. The problem that I'm facing is that I only want to show the notification if the app is currently not open/shown. If it's open I want to show an AlertDialog instead.
I know that it might be a better idea to put only the plain content of the notification into the intent and only build it when it should be displayed and I want to refactor that later on.
My main problem is, how do I determine in the onReceive of my broadcast receiver if the app is currently showing to decide if a notification or an alert should be displayed?
Or is there an entirely different approach that might work better (for example using WorkManager)?
I think you can handle it on your BroadcastReceiver
public void onReceive(Context context, Intent intent) {
if (isForeground(context))
// AlertDialog
else
// Notification
}
public boolean isForeground(Context mContext) {
ActivityManager activityManager = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.AppTask> tasks = activityManager.getAppTasks();
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).getTaskInfo().topActivity;
return topActivity.getPackageName().equals(mContext.getPackageName());
}
return true;
}
i am making an app that required to inform the user with a notification when a specific date is approaching.
I use a Client class as a 'middle man' between activity and a Service. Once i bind the service, i call a method that interacts with AlarmTask class that uses AlarmManager to set an alarm. And as a last step i send a PendingIntent to start my another class witch is a BroadcastReceiver for my notification.
My problem is that the onReceive() is not called. Code reaches all the way to alarmManager.set() correctly. I read many posts and tried different ways to register my BroadcastReceiver. Any ideas on what might be wrong?
AlarmTask
public class AlarmTask implements Runnable {
private final Calendar date;
private final AlarmManager alarmManager;
private final Context context;
private long mGoalId;
public AlarmTask(Context context, Calendar date, long goalId) {
this.context = context;
this.alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
this.date = date;
mGoalId = goalId;
}
#Override
public void run() {
Log.e("AlarmTask", "run executed with request code: " + mGoalId);
// Request to start the service when the alarm date is upon us
Intent intent = new Intent(context, NotificationReceiver.class);
intent.putExtra(NotificationReceiver.INTENT_NOTIFY, true);
// mGoalId is the unique goal id that is gonna be used for deletion
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, (int)mGoalId, intent, 0);
alarmManager.set(AlarmManager.RTC, date.getTimeInMillis(), pendingIntent);
}
}
NotificationReceiver
public class NotificationReceiver extends BroadcastReceiver {
// Unique id to identify the notification.
private static final int NOTIFICATION = 123;
public static final String INTENT_NOTIFY = "com.test.name.services.INTENT_NOTIFY";
private NotificationManager notificationManager;
#Override
public void onReceive(Context context, Intent intent) {
Log.e("onReceive", "Broadcast fired : " + intent);
CharSequence title = "Alarm!!";
int icon = R.drawable.goal;
CharSequence text = "Your notification time is upon us.";
long time = System.currentTimeMillis();
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(icon);
builder.setContentTitle(title);
builder.setContentText(text);
builder.setVibrate(new long[] { 0, 200, 100, 200 });
final Notification notification = builder.build();
notificationManager.notify(NOTIFICATION, notification);
}
}
Manifest
<receiver android:name=".broadcasts.NotificationReceiver"></receiver>
i have a notification in android, and i want to do something like
MyStaticClass.start() when i click in the notification, this class is not an activity, its only to instantiate a needed class.
How can i do it it?
This is my actually code:
String notificationContent = "Hay una nueva version disponible";
String notificationTitle = "Firext";
Bitmap largeIcon = BitmapFactory.decodeResource(FirextApplication.getInstance().getResources(), R.drawable.ic_launcher);
int smalIcon = R.drawable.ic_launcher;
Intent intent = new Intent(FirextApplication.getInstance(), UpdateProcess.class);
intent.setData(Uri.parse("content://" + Calendar.getInstance().getTimeInMillis()));
PendingIntent pendingIntent = PendingIntent.getActivity(FirextApplication.getInstance(), 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);
NotificationManager notificationManager = (NotificationManager) FirextApplication.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
FirextApplication.getInstance())
.setWhen(Calendar.getInstance().getTimeInMillis())
.setContentText(notificationContent)
.setContentTitle(notificationTitle)
.setSmallIcon(smalIcon)
.setAutoCancel(true)
.setTicker(notificationTitle)
.setLargeIcon(largeIcon)
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)
.setContentIntent(pendingIntent);
Notification notification = notificationBuilder.build();
notificationManager.notify((int) Calendar.getInstance().getTimeInMillis(), notification);
And the class i need to call
public class UpdateProcess {
private static final String APPLICATION_VND_ANDROID_PACKAGE_ARCHIVE = "application/vnd.android.package-archive";
private static final String NEWVERSION = "NewVersion.apk";
private static final File EX = Environment.getExternalStorageDirectory();
private static volatile UpdateProcess instance = null;
private Activity activity;
private UpdateProcess(Activity activity) {
this.activity = activity;
checkForNewUpdates();
}
public static void startUpdate(Activity activity) {
if (instance == null) {
synchronized (UpdateProcess.class) {
if (instance == null) {
instance = new UpdateProcess(activity);
}
}
} else {
UpdateProcess.instance.activity = activity;
}
}
I only need do a UpdateProcess.startUpdate();
Edit:
This is my new code:
private void showNot() {
String notificationContent = "Hay una nueva version disponible";
String notificationTitle = "Firext";
Bitmap largeIcon = BitmapFactory.decodeResource(FirextApplication.getInstance().getResources(), R.drawable.ic_launcher);
int smalIcon = R.drawable.ic_launcher;
Intent intent = new Intent(FirextApplication.getInstance(), UpdateProcess.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
Notification notificationBuilder = new NotificationCompat.Builder(context)
.setWhen(Calendar.getInstance().getTimeInMillis())
.setContentText(notificationContent)
.setContentTitle(notificationTitle)
.setSmallIcon(smalIcon)
.setTicker(notificationTitle)
.setLargeIcon(largeIcon)
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)
//.setContentIntent(pendingIntent)
.addAction(android.R.drawable.arrow_up_float, "Call", pendingIntent).build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder);
}
public class UpdateProcess extends IntentService {
private static final String APPLICATION_VND_ANDROID_PACKAGE_ARCHIVE = "application/vnd.android.package-archive";
private static final String NEWVERSION = "NewVersion.apk";
private static final File EX = Environment.getExternalStorageDirectory();
private static volatile UpdateProcess instance = null;
private Context context;
private UpdateProcess(Context context) {
super("UpdateProcess");
this.context = context;
checkForNewUpdates();
}
#Override
protected void onHandleIntent(Intent intent) {
startUpdate(getApplicationContext());
}
But when i do click in action nothing happens..i get thi error:
05-10 17:15:22.955 753-7617/? W/ActivityManager﹕ Unable to start service Intent { flg=0x10000000 cmp=com.firext.android/.util.UpdateProcess bnds=[192,346][1080,490] } U=0: not found
Edit 2. Now works, i forget include service in manifest
I need to show a toast message in a method inside UpdateProcess, what i should use?
Your Notification can trigger an Activity, a Service, or a BroadcastReceiver via its PendingIntent. Activity is a Context. Service is a Context. BroadcastReceiver receives a Context as a parameter to onReceive(). Hence, from all three locations, you already have a Context, so use that one.
You might also consider getting rid of the Toast entirely. A Toast is only good for a message that the user does not need, as the user might not see it.
I only need do a UpdateProcess.startUpdate();
It would appear that checkForNewUpdates() will do some significant work.
If that work will take less than ~15 seconds, convert UpdateProcess to UpdateIntentService, extending IntentService, and do your checkForNewUpdates() work in onHandleIntent(). You can use a getService() PendingIntent to trigger the UpdateIntentService to do its work.
If the work is likely to take longer than that, you should consider using the WakefulBroadcastReceiver pattern (or perhaps my WakefulIntentService), to ensure that the device stays awake long enough for your work to complete.
Instead to use getActivity() use getService() in you pending intent. Create an ad-hoc intent service to call UpdateProcess.startUpdate() in onHandleIntent(). In this case you can't use an activity as parameter of course, but maybe you can change the parameter in a Context.
Trying to cancel an AlarmManager alarm, isn't quite working.
I create a PendingIntent like so:
static PendingIntent makePendingIntent(int id, Bundle bundle)
{
Intent intent = new Intent(mContext.getApplicationContext(), mfLocalNotificationManager.class);
if(bundle != null)
{
intent.putExtra(BUNDLE_ID, bundle);
}
return PendingIntent.getBroadcast(mContext.getApplicationContext(), id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
Called from SendLocalNotification:
public static int SendLocalNotification(String title, String text, String tickerText,
int timeSent, int timeOffset, String sound)
{
AlarmManager alarmMgr =
(AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
Bundle notificationData = new Bundle();
notificationData.putString("title", title);
notificationData.putString("text", text);
notificationData.putString("tickerText", tickerText);
/*snip, bundle stuff*/
int noteID = title.hashCode();
notificationData.putInt("noteID", noteID);
PendingIntent pendingIntent = makePendingIntent(noteID, notificationData);
if( pendingIntent == null )
{
//This should probably be flagged as an error or an assertion.
Log.d("[MF_LOG]", "Java intent is null");
return -1;
}
//This isn't my timing code, don't hate me for it
Calendar time = Calendar.getInstance();
time.setTimeInMillis(System.currentTimeMillis());
time.add(Calendar.SECOND, timeOffset);
alarmMgr.set(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(),
pendingIntent);
return noteID;
}
And attempt to cancel it like so (passing the id we previously got back from SendLocalNotification):
public static void CancelNotification(int id)
{
String ns = Context.NOTIFICATION_SERVICE;
AlarmManager alarmMgr =
(AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = makePendingIntent(id, null);
//This doesn't work...
pendingIntent.cancel(); //<- added based on SO post, doesn't help
alarmMgr.cancel(pendingIntent);
}
Nothing seems to work, and I've followed as much of the advice on other posts as I can find (making sure the ID is the same, ensuring the PendingIntent is recreated exactly the same) and it still seems to crash. As a side note, trying to check if a notification exists using the flag PendingIntent.FLAG_NO_CREATE also doesn't work, creating a new object instead of returning null.
The device I'm testing this on is a Nexus 7 and I'm building against API 9.
You need to recreate your intent and CANCEL it like this:
AlarmManager alarm = (AlarmManager) context.getSystemService(ALARM_SERVICE);
PendingIntent peding = PendingIntent.getActivity(context, code, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
alarm.cancel(peding);
I've found many similar questions to this, but they're too complicated (too much code), at least I think.
Can this thing be done in a few code of lines? I want to fire an activity in 10 (let's say) minutes, that's it. Thank you.
To Set Alarm for 10 Minutes(let's say) Use this code
AlarmManager alarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);
Intent intent = new Intent(this, ShortTimeEntryReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),10*60*1000, pendingIntent);
To Start Activity
public class ShortTimeEntryReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
try {
Bundle bundle = intent.getExtras();
String message = bundle.getString("alarm_message");
// Your activity name
Intent newIntent = new Intent(context, ReminderPopupMessage.class);
newIntent.putExtra("alarm_message", message);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(newIntent);
} catch (Exception e) {
e.printStackTrace();
}
}
}
In your Manifest File Add the following
<receiver android:name=".ShortTimeEntryReceiver"
android:enabled="true"
android:process=":remote">
</receiver>
This function I use sets or cancels an alarm depending on the "Set" parameter
public static void SetAlarm(Context c, long AlarmTime, int ItemID, String Message, Boolean Set) {
Intent intent = new Intent(c, AlarmReceiver.class);
intent.putExtra("Message", Message);
intent.putExtra("ItemID", ItemID);
PendingIntent sender = PendingIntent.getBroadcast(c, 8192 + ItemID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Clear the seconds to 0 for neatness
Calendar ca = Calendar.getInstance();
ca.setTimeInMillis(AlarmTime);
ca.set(Calendar.SECOND, 0);
AlarmTime = ca.getTimeInMillis();
// Get the AlarmManager service
AlarmManager am = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
if (Set) {
am.set(AlarmManager.RTC_WAKEUP, AlarmTime, sender);
} else {
am.cancel(sender);
}
}
You would then need a Broadcast Receiver to handle the alarm and do whatever it is you want to do.
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
Bundle bundle = intent.getExtras();
String Message = bundle.getString("Message");
int ItemID = bundle.getInt("ItemID");
// Do what you want to do, start an activity etc
} catch (Exception e) {
e.printStackTrace();
}
}
}