Modify static BlockingQueue from PendingIntent triggered by AlarmManager - android

In my android application I have a "global" application class and inside it, among others, I have a static BlockingQueue in which I store information. When the queue is full I flush it to file. I have multiple producers, Services, BroadcastReceivers and everything works fine. Now I have to add an alarm that every 30 minutes triggers a PendingIntent from which I have to write to this BlockingQueue and this doesn't work! I see that the appropriate functions are called but the data is not written. If I Log.d() it before blockingQueue.put() I can see the data and then it get lost. I do the same procedure from everywhere in my app and it only does not work from the BroadcastReceiver of the PendingIntent of the Alarm. For sure I'm missing something. What can I do?
Here's how I trigger the Alarm from a Service (and it works):
alarmNotificationIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, QuestionnaireNotificationReceiver.class), 0);
if(alarmNotificationIntent!=null) {
if (alarmManager != null) {
if (iLogApplication.isAtLeastMarshmallow()) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + Constants.NOTIFICATION_INTERVAL, alarmNotificationIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + Constants.NOTIFICATION_INTERVAL, alarmNotificationIntent);
}
}
}
And this is the BroadcastReceiver:
public class QuestionnaireNotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(context.toString(), "Questionnaire notification");
//this method does blockingQueue.put(answer.toString());
iLogApplication.persistInMemoryAnswerQuestionnaireEvent(new Answer(new Question(666)));
System.out.println(iLogApplication.questionnaire.toString());
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.PACKAGE_NAME, Context.MODE_PRIVATE);
Intent notificationIntent = new Intent(context, NotificationActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, (int) System.currentTimeMillis(), notificationIntent, 0);
iLogApplication.questionnaireBuilder.setContentTitle("Nuova domanda disponibile")
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.ic_notification_bar)
.setAutoCancel(false)
.setOngoing(true);
String notificationText = "Hai %d domand%c a cui rispondere";
if (iLogApplication.questionnaireBuilder != null) {
iLogApplication.questionnaireBuilder.setWhen(System.currentTimeMillis());
if(iLogApplication.questionnaire.getNumberOfQuestions()>2) {
iLogApplication.questionnaireBuilder.setContentText(String.format(notificationText, iLogApplication.questionnaire.getNumberOfQuestions(), 'e'));
}
else {
iLogApplication.questionnaireBuilder.setContentText(String.format(notificationText, iLogApplication.questionnaire.getNumberOfQuestions(), 'a'));
}
}
if (iLogApplication.notificationManager != null) {
iLogApplication.notificationManager.notify(Constants.QUESTIONNAIRENOTIFICATIONID, iLogApplication.questionnaireBuilder.build());
}
PendingIntent alarmNotificationIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, QuestionnaireNotificationReceiver.class), 0);
if(alarmNotificationIntent!=null) {
if(iLogApplication.alarmManager!=null) {
if (iLogApplication.isAtLeastMarshmallow()) {
iLogApplication.alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+ Constants.NOTIFICATION_INTERVAL, alarmNotificationIntent);
} else {
iLogApplication.alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+ Constants.NOTIFICATION_INTERVAL, alarmNotificationIntent);
}
}
}
}
}
Since I want my alarm to work also in Marshmallow Doze mode I have to use setExactAndAllowWhileIdle() and recall the method every time the Broadcast is triggered.

The problem was the PendingIntent. In the manifest I declared it like this:
<receiver android:process=":remote" android:name=".broadcastreceivers.QuestionnaireNotificationRunnable"></receiver>
the flag android:process=":remote" forces it to run on a different process. By removing it everything started working perfectly.

Related

Alarm not triggering in android

I am new to android programming. What am i doing wrong? AlarmReceiver class not triggering...I want to trigger repeatedly the AlarmReceiver.class ...
I used the code from this page https://developer.android.com/training/scheduling/alarms#precision
OnCreate at MainActivity.java i have the following code
AlarmManager alarmManager = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this,AlarmReceiver.class);
//Check if the PendingIntent already exists
PendingIntent pendingIntent =
PendingIntent.getService(this, 6661, intent,
PendingIntent.FLAG_NO_CREATE);
if (pendingIntent != null && alarmManager != null) {
//cancel here if you want
}
//Run every 60 seconds!
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 60000,
60000, pendingIntent);
The AlarmReceiver.java class includes the following code...
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// For our recurring task, we'll just display a message
Log.i("logit","testing...");
}
}
Do you have an entry in your manifest for the receiver like that?
<receiver android:name=".AlarmReceiver"></receiver>

AlarmManager is unstable

I used AlarmManager to set time and expect to do some work in the future. Almost case it work well without problem. But sometimes (just sometimes), the alarm not fired. It is difficult to reproduce the issue and I still do not know the reason.
I got this issue on several OS version : 4.4, 5.1, 6.0, 6.1, 7.0.
I already used WakefulBroadcastReceiver to start a service with Wakelock, but the issue still happen.
Below is my code to schedule a alarm.
Intent alarmIntent = new Intent(context, AlarmReceiver.class);
alarmIntent.putExtra("todo_id", myTodo.getId());
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, myTodo.getId(), alarmIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(calendar.getTimeInMillis(), pendingIntent);
alarmManager.setAlarmClock(alarmClockInfo, 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);
}
public class AlarmReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
LogUtil.debug("onReceive()");
if (null != intent.getExtras()) {
int id = intent.getIntExtra("todo_id", -1);
if (id != -1) {
Intent myIntent = new Intent(context, AlarmService.class);
myIntent.putExtra("todo_id", id);
LogUtil.debug("Receiver receive todo id: "+id );
startWakefulService(context, myIntent);
}
}
}
}
public class AlarmService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.debug("Service onStartCommand");
int id = intent.getIntExtra("todo_id", -1);
LogUtil.debug("Service receive todo id: " + id);
// do some stuff here
AlarmReceiver.completeWakefulIntent(intent);
return START_REDELIVER_INTENT;
}
Does anyone have the same issue like me ? And what is your solution ?
Maybe this issue come from Android SDK, they made AlarmManager work unstable.
I had the same issue , i solved this issue by doing all my logic in AlarmReceiver. I was also starting service in receiver which hold my alarm logic. But once i move my code into alarm receiver it works fine. Mine issue was i wasn't ending my service , it kept on running and causing some issue or might be other reasons but it solved my issue. You should try and let me know if this help.

Android getExtra() always gets default value in service

I am making an alarm, and service is started by the alarm. I want services to receive Extra data so with googling I set the flag as Intent.FILL_IN_DATA. Below is my code.(Only important part is shown)
(I have several alarm mode, and different services are called)
public static void setAlarm(Context mContext, int mode){
if(mode==A){
intent = new Intent(mContext, AService.class);
}
else{
intent = new Intent(mContext, BService.class);
intent.putExtra(NOTI_MODE, mode);
}
PendingIntent pIntent = PendingIntent.getService(mContext, 0, intent,Intent.FILL_IN_DATA);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
Log.d("set alarm","alarm 4.4");
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pIntent);
} else {
Log.d("set alarm","alarm 4.4 under");
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pIntent);
}
}
public static void cancelAlarm(Context mContext){
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
PendingIntent pIntent = PendingIntent.getService(mContext, 0, new Intent(mContext, AService.class),Intent.FILL_IN_DATA);
alarmManager.cancel(pIntent);
pIntent = PendingIntent.getService(mContext, 0, new Intent(mContext, BService.class),Intent.FILL_IN_DATA);
alarmManager.cancel(pIntent);
}
service side code(edit: this code is in BService class. AService does not need to receive Extras.)
public int onStartCommand(Intent intent, int flags, int startId) {
int notiMode=intent.getIntExtra(AlarmManagerHelper.NOTI_MODE, -1);
}
However, the result is always -1, which means no Extras is being received.
What is the problem with my code? I'm googling for hours and no hope.
Please help if you have any ideas.
Thanks in advance.
EDIT: setAlarm method is in
public class AlarmManagerHelper extends BroadcastReceiver{} class.
and below is from AndroidManifest
<receiver android:name="com.example.test.AlarmManagerHelper" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
When I use
pIntent = PendingIntent.getService(mContext, 0, new Intent(mContext, BService.class), 0);
instead of
pIntent = PendingIntent.getService(mContext, 0, new Intent(mContext, BService.class),Intent.FILL_IN_DATA);
at cancelAlarm(), service gets Extras all right (but the alarm does not cancel of course)
I don't get why setAlarm() does not work when I change cancelAlarm()..
I found the solution. Quite embarrassing.. I should have searched more for myself before posting the question...
Changed Intent.FILL_IN_DATA to PendingIntent.FLAG_UPDATE_CURRENT and now it works like charm.
Does not know why, but if you are suffering from same problem, please try this solution :)
According to the code you posted, you have two services (AService and BService).
I am guessing that the code snippet you are sharing is for your AService?
I think the top part of your setAlarm method should be:
public static void setAlarm(Context mContext, int mode)
{
if(mode==A)
{
intent = new Intent(mContext, AService.class);
// NOTE: Add this line!
intent.putExtra(NOTI_MODE, mode);
}
else
{
intent = new Intent(mContext, BService.class);
intent.putExtra(NOTI_MODE, mode);
}
// snipped rest of code in this method
}

PendingIntent.getBroadcats don't find an Intent

I'm writing an application with multiple alarms and I want to have an option to cancel them. I'm using AlarmManager and PendingIntent. My problem is that when I want to cancel an alarm, getBroadcast don't find a PendingIntent matching the criteria.
private void turnOnMorningRemider()
{
long time = System.currentTimeMillis() + 60 * 45;
Intent tmpIntent = new Intent(this, AlarmPopUpDialog.class);
tmpIntent.putExtra(getString(R.string.alarm_time_of_day), AlarmPopUpDialog.REQUEST_CODE_MORNING);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntent(tmpIntent);
// Gets a PendingIntent containing the entire back stack
morningPendingIntent = stackBuilder.getPendingIntent(AlarmPopUpDialog.REQUEST_CODE_MORNING, PendingIntent.FLAG_ONE_SHOT);
//set the alarm for particular time
alarmManager.set(AlarmManager.RTC_WAKEUP, time, morningPendingIntent);
Toast.makeText(this, "Alarm is on.", Toast.LENGTH_LONG).show();
}
To cancel alarm I use:
private void turnOffMorningReminder()
{
Intent tmpIntent = new Intent(this, AlarmPopUpDialog.class);
PendingIntent pi = PendingIntent.getBroadcast(this, AlarmPopUpDialog.REQUEST_CODE_MORNING,
tmpIntent, PendingIntent.FLAG_NO_CREATE);
if (pi != null)
{
alarmManager.cancel(pi);
Toast.makeText(this, "Alarm is off.", Toast.LENGTH_LONG).show();
}
}
The pi variable is always null, so alarm starts ringing.
The next problem is, when I try to create PendingIntent with getBroadcast (instead of stack builder) my alarm never activates.
morningPendingIntent = PendingIntent.getBroadcast(this, AlarmPopUpDialog.REQUEST_CODE_MORNING, tmpIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Is using the stack builder the reason why i can't find the intent?
I feel stupid. My problem was that my AlarmPopUpDialog extended Activity not BroadcastReceiver class. When I added a class that extened BroadcastReceiver in the middle everything started to work.
morningPendingIntent = PendingIntent.getBroadcast(this, AlarmBrodcastReceiver.REQUEST_CODE_MORNING, tmpIntent, PendingIntent.FLAG_UPDATE_CURRENT);
and
public class AlarmBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent)
{
Intent i = new Intent(context, AlarmPopUpDialog.class );
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtras(intent);
context.startActivity(i);
}
}
I hope that this answer will help the next poor guy.

One time alarm not working

I have done some research and tried to implement a one time alarm that sends a notification to the user, but for some reason I cannot understand, when the time comes, the alarm is not being activated. I think the onReceive method is not being called, but I don't know why, since it's the first time I try to implement an alarm.
=== Edit: it seems that the alarm's onReceive is working after all, I got the toast message "Alarm!!" at the right time (don't know why it didn't the first time I tested), but no notification was received, though... Any clues?
This is the code for the Alarm class:
public class Alarm extends BroadcastReceiver {
public static final String PREFS_FILE_NAME = MainActivity.PREFS_FILE_NAME;
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
// send notification
Toast.makeText(context, "Alarm!!", Toast.LENGTH_SHORT).show();
// API < 16 so have to use compat
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setContentTitle(context.getResources().getString(R.string.memo_test_ready))
.setContentText(context.getResources().getString(R.string.click_to_start));
Intent resultIntent = new Intent(context, UpcomingTest.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(UpcomingTest.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(123, mBuilder.build());
}
public void Set(Context context)
{
DatabaseHandler db = new DatabaseHandler(context);
MemoryTestLevel memoTestLevel = new MemoryTestLevel();
long timeInterval;
long memoryTest_dateTime;
String alarmMemoLevel;
List<Phrase> studyPhrasesList = db.getPhrasesWithState(Phrase.START_STUDYING);
if (studyPhrasesList.size() > 0 ){ // if there are any phrases here, update them; dateTime == level 1
alarmMemoLevel = "Level 1 ";
memoTestLevel = db.getMemoryTestLevelWithLevel(MemoryTestLevel.LEVEL_1);
timeInterval = memoTestLevel.getTimeInterval();
TestDateTimeCalculator datetimeCalc = new TestDateTimeCalculator();
memoryTest_dateTime = datetimeCalc.calculate(timeInterval);
for(Phrase phrase : studyPhrasesList){
phrase.setMemoryTestPending(memoTestLevel.getMemoryTest_id(), memoryTest_dateTime);
db.updatePhrase(phrase);
}
} else {
// if there are no phrases to be set at level 1, then get the lowest memoTest_datime of
// the ones that are pending
alarmMemoLevel = "next after L1 ";
memoryTest_dateTime = db.getLowestMemoTestDateTime();
}
/* 2. Set new Alarm
* 2.1. Determine which phrases will go into this new alarm */
List<Phrase> nextMemoryTestPhrases = db.getNextMemoryTestPhrases(memoryTest_dateTime);
for(Phrase phrase : nextMemoryTestPhrases) {
phrase.setState(Phrase.MEMORY_TEST_SCHEDULED);
db.updatePhrase(phrase);
}
// 2.3 set alarm for required dateTime
AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, Alarm.class);
PendingIntent pendingInt = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmMgr.set(AlarmManager.RTC_WAKEUP, memoryTest_dateTime, pendingInt);
// 2.4 save the alarm.dateTime in the preferences so it can be used in the "upcoming test" activity
SharedPreferences preferences = context.getSharedPreferences(PREFS_FILE_NAME, Context.MODE_PRIVATE);
Editor editor = preferences.edit();
editor.putLong("NEXT_TEST_DATETIME", memoryTest_dateTime);
SimpleDateFormat sdf = new SimpleDateFormat("EEE, MMM d, yyyy hh:mm");
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(memoryTest_dateTime);
String nextTestDateStr = sdf.format(calendar.getTime());
Toast.makeText(context, "Alarm set to " + alarmMemoLevel + nextTestDateStr, Toast.LENGTH_LONG).show();
editor.commit();
}
public void Cancel(Context context)
{
Intent intent = new Intent(context, MyBroadcastReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(sender);
}
}
And I have a broadcast receiver in case the phone is rebooted:
public class MyBroadcastReceiver extends BroadcastReceiver {
public static final String PREFS_FILE_NAME = MainActivity.PREFS_FILE_NAME;
#Override
public void onReceive(Context context, Intent intent) {
// TODO setup alarm again (get datetime from system)
Alarm alarm = new Alarm();
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
SharedPreferences preferences = context.getSharedPreferences(PREFS_FILE_NAME, Context.MODE_PRIVATE);
long nextTestDateInMillis = preferences.getLong("NEXT_TEST_DATETIME", 0);
if(nextTestDateInMillis > 0){
alarm.Set(context);
}
}
}
}
From the research I found, it seems that something is missing/wrong in my manifest. This is what I have there:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>
The below is inside application:
<receiver android:name="liliana.phrasememo.util.MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:process=":remote" android:name="liliana.phrasememo.util.Alarm"/>
Also, if you see anything else that's wrong in my implementation of Alarm + Notification it would be brilliant to give me the heads up. Thank you very much :-)
to set alarm may be this code helps you
Intent myIntent = new Intent(yourcontext, Alarm.class);
PendingIntent pendingIntent = PendingIntent.getService(act, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager)act.getSystemService(act.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE,22);
calendar.set(Calendar.SECOND, 0);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
also you must implement service instead of broadcastreciever to run at alarm time
Thanks very much to answer posted here, what is missing in my code is the notification icon, which I have added like this:
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher) // this was missing
.setContentTitle(context.getResources().getString(R.string.memo_test_ready))
.setContentText(context.getResources().getString(R.string.click_to_start));
And in fact, if we look at the documentation, it does specify that the small icon must be in the notification:
Required notification contents
A Notification object must contain the following:
A small icon, set by setSmallIcon()
A title, set by setContentTitle()
Detail text, set by setContentText()
The LogCat does have a file not found error:
05-05 12:39:39.191: A/NetworkStats(89): Caused by: java.io.FileNotFoundException: /proc/net/xt_qtaguid/stats: open failed: ENOENT (No such file or directory)
But it doesn't cause any crashes so you might not realise it. I'm not sure anything could be added to the notification constructor itself that would allow the user to see right away that something is missing. I don't have very advanced Java knowledge.

Categories

Resources