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>
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;
}
guys, I am developing an alarm app in which alarm is triggering good and at the right time but the drawback is when the alarm is ringing with an activity then when we press the home button too it is ringing the real problem is when the application is closed by pressing the home button and pressing or swiping close all apps the application getting destroyed I need something like Google does like showing a notification and playing sound even if the application is closed.
So I think someone is having Idea about this issue.
Here is what from Google Clock
Googel Clock image
Because of android Q, I had developed something that when the user is alive at the time of alarm then we just show notification with the help of this.
public class DismissAlarmNotificationController {
public final int NOTIFICATION_ID = 1;
public static final String INTENT_KEY_NOTIFICATION_ID = "notificationId";
public final String CHANNEL_ID = "channel-01";
private NotificationManager notificationManager;
private Context context;
private final int IMPORTANCE = NotificationManager.IMPORTANCE_HIGH;
public DismissAlarmNotificationController(Context context) {
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
this.context = context;
}
public void showNotification() {
Intent fullScreenIntent = new Intent(context, DismissAlarmActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(context, 0,
fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(
CHANNEL_ID, getChannelName(), IMPORTANCE);
notificationManager.createNotificationChannel(notificationChannel);
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_alarm_on_notification)
.setContentTitle(context.getString(R.string.dismiss_alarm_notification_title))
.setContentText(context.getString(R.string.dismiss_alarm_notification_body, getCurrentTime()))
.setAutoCancel(true)
.addAction(getDismissNotificationAction())
.setFullScreenIntent(fullScreenPendingIntent, true);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
}
public String getChannelName() {
return context.getString(R.string.app_name) + "Channel";
}
public void cancelNotification() {
notificationManager.cancelAll();
}
private NotificationCompat.Action getDismissNotificationAction() {
Intent dismissIntent = new Intent(context, DismissNotificationReceiver.class);
dismissIntent.putExtra(INTENT_KEY_NOTIFICATION_ID, NOTIFICATION_ID);
PendingIntent dismissNotificationPendingIntent = PendingIntent.getBroadcast(context, 0, dismissIntent, PendingIntent.FLAG_CANCEL_CURRENT);
return new NotificationCompat.Action.Builder(
0,
context.getString(R.string.dismiss_alarm_notification_dismiss_button_title),
dismissNotificationPendingIntent)
.build();
}
private String getCurrentTime() {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm");
Date currentTime = new Date();
return dateFormat.format(currentTime);
}
}
Thanks in advance.
Give a try to Android's WorkManager!
It lets you run background works even if the app is closed or the phone has been restarted.
Hope it will help!
Created new Java class where trying to implement AlarmManager class method. I want to create functionality for creating, editing and deleting alarms. Everything has worked fine while all the code was in MainActivity class.
New class has constructor where context is passed. While application builds successfully, the alarm is not working.
I'm calling setShowDataEvent method at MainActivity:
AlarmController alarmController = new AlarmController(this);
alarmController.setShowDataEvent(startTime, finishTime);
Here is AlarmController class:
private final String TAG = "AlarmController";
Context context;
PendingIntent pendingIntent;
AlarmManager mAlarmManager;
public AlarmController(Context context){
this.context = context;
this.mAlarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
}
public void setShowDataEvent(long startTime, long finishTime){
newAlarmIntentData(startTime);
mAlarmManager.setExact(AlarmManager.RTC, startTime, pendingIntent);
newAlarmIntentData(finishTime);
mAlarmManager.setExact(AlarmManager.RTC, finishTime, pendingIntent);
Log.d(TAG, "setShowDataEvent has been executed");
}
private void newAlarmIntentData(long time){
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra("showData", true);
intent.putExtra("time", time);
pendingIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_ONE_SHOT);
}
After moving AlarmManager to constructor and restarting AndroidStudio all begin to work. I have added same data to intent, so now there are a two different PendingIntent's.
intent.setData(Uri.parse("myalarms://" + time));
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);
}
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.