Ensuring Alarms Work after App Update - java

My application is almost ready and now one thing which I got to know that after update, all alarms set by user will go off. For that I read a lot and found I have to user package replace in manifest to check update and receive in broadcast. Here is what I added in manifest and tested as it sent broadcast when I change versioncode in gradle.
<receiver
android:name=".AppUpdateHandler"
android:exported="false">
<intent-filter android:priority="900">
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
<intent-filter android:priority="1000">
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
Now next is the code which I used to set alarms,
public void setalarm() {
Intent intent = new Intent(Settings.this, AlarmReceiver.class);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AlarmReceiver.receivercode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
userdefinedinterval = sharedPreferences.getString("date", "");
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), Integer.parseInt(userdefinedinterval) * 60 * 60 * 24 * 1000, pendingIntent);
}
I read and got to know about pending intent ids, which I my case is AlarmReceiver.receivercode. But now need help to what should I do to keep my app alarms active after update. Do I need to call all alarms via broadcast which is capturing update status or something else is possible. One more thing, I have added boot completed in manifest too so that alarm keep working after reboot. Here I m checking status of each option associated with alarm via sharedpreference and then trigger alarm respectively. Do I need to handle update in same way?

I got solution of it. It can be done in same manner as we do for boot completed. Just call bootcompleted receiver class on update and all alarms will be reset as per user settings.
Or just copy alarm method in update receiver and its is done.

Related

Unique PendingIntent with Manifest declaration

I am creating an Android application which programmatically creates alarms which persist when the application is closed. In order to have the alarms persist, I had to define the receiver in my Manifest like so:
<receiver
android:name="com.blah.blah.AlarmReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter android:priority="999" >
<action android:name="com.blah.blah.ALARM" />
</intent-filter>
</receiver>
Here is how I am currently creating the PendingIntents..
Intent intent = new Intent(ACTION_ALARM);
PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager manager = (AlarmManager (this.getSystemService(Context.ALARM_SERVICE ));
Since I am using the constant ACTION_ALARM, all alarms are connected to the same PendingIntent, so calling .cancel() on that deletes all alarms. However, I want to delete specific alarm instances. The only way I know of to create a unique PendingIntent is to specify a unique action, which conflicts with defining my Receiver in the Manifest as I have above. Is there any other way to make distinguishable PendingIntents for my alarms? I tried adding Data as well, but it seemed the Receiver in the Manifest was not being triggered.
Alternatively, if I start a receiver for each alarm programmatically, is there a way to have those persist when the application is closed?
Thanks.
Found the solution here: How to create different pendingintent so filterEquals() return false?
Using the second parameter of PendingIntent.getBroadcast works to establish a unique PendingIntent. There is no need for a unique Action, though that is what every other post I could find suggested.
Not sure how to close the question now...

Notification DeleteIntent broken on later versions of Android

In our app OneBusAway Android (open-source on Github), we need to be notified when the user dismisses a particular reminder notification, so we don't post another reminder notification for the same event (how long until their bus arrives).
We're doing this by listening for an Intent in our app, registered as the DeleteIntent with the Notification. When the user dismisses the notification (either by swiping it away, or tapping the clear button in the notification window), our app should receive that Intent.
From testing, it seems that with the current version on Google Play (and the current master branch on Github), the DeleteIntent is never received in our application in the following versions of Android:
Android 4.4.3
Android 4.4.4
However, the exact same code DOES work (i.e., the Intent registered as the DeleteIntent is received by the app) on:
Android 2.3.3
Android 2.3.6
Android 4.1.1
Android 4.1.2
I've looked at the following SO posts that deal with DeleteIntent, and none of the solutions listed work on Android 4.4.3 and 4.4.4:
Notification Auto-Cancel does not call DeleteIntent
Android - DeleteIntent, how to use?
Notification deleteIntent does not work
https://stackoverflow.com/questions/24218626/how-to-detect-notification-cancel-event-in-android-not-deleteintent
https://stackoverflow.com/questions/22769523/why-my-deleteintent-is-not-working-on-my-notification
Android deleteIntent not working? What's wrong with my code?
Custom actions using implicit intents between applications
The current working master branch uses a Service to listen for the Intent. However, based on some of the above posts, I did tweak some of the code to be more in line with working examples that use a BroadcastReceiver to listen for the Intent.
The code using the BroadcastReceiver is in the following Github branch:
https://github.com/CUTR-at-USF/onebusaway-android/tree/issue104-RepeatingReminders
Below are excerpts for what my current version looks like (that still works on Android 4.1.2 and lower, but not 4.4.3 or 4.4.4), along with links to Github source:
Creating the notification
https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/java/com/joulespersecond/seattlebusbot/tripservice/NotifierTask.java#L131
private Notification createNotification(Uri alertUri) {
//Log.d(TAG, "Creating notification for alert: " + alertUri);
Intent deleteIntent = new Intent(mContext, AlarmReceiver.class);
deleteIntent.setAction(TripService.ACTION_CANCEL);
deleteIntent.setData(alertUri);
return new NotificationCompat.Builder(mContext)
.setSmallIcon(R.drawable.ic_stat_notification)
.setDefaults(Notification.DEFAULT_ALL)
.setOnlyAlertOnce(true)
.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build();
}
Title and other dynamic notification info are set a few lines later (and reset later, if the notification remains undismissed):
#SuppressWarnings("deprecation")
private void setLatestInfo(Notification notification,
String stopId,
String routeId,
long timeDiff) {
final String title = mContext.getString(R.string.app_name);
final PendingIntent intent = PendingIntent.getActivity(mContext, 0,
new ArrivalsListActivity.Builder(mContext, stopId).getIntent(),
PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(mContext,
title,
getNotifyText(routeId, timeDiff),
intent);
}
TripService contains the constants for the action:
public static final String ACTION_CANCEL =
"com.joulespersecond.seattlebusbot.action.CANCEL";
AlarmReceiver
https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/java/com/joulespersecond/seattlebusbot/AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
private static final String TAG = "AlarmReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "In onReceive with intent action " + intent.getAction());
...
}
}
AndroidManifest
https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/AndroidManifest.xml
<receiver android:name=".AlarmReceiver">
<!-- These action names must match the constants in TripService -->
<intent-filter>
<action android:name="com.joulespersecond.seattlebusbot.action.SCHEDULE" />
<action android:name="com.joulespersecond.seattlebusbot.action.POLL" />
<action android:name="com.joulespersecond.seattlebusbot.action.CANCEL" />
</intent-filter>
</receiver>
With the above, on Android 4.4.3/4.4.4, the AlarmReceiver never sees the Intent when the user dismisses the notification.
I also tried adding a MIME type, as specified in Custom actions using implicit intents between applications, but that didn't work on Android 4.4.3/4.4.4 either:
Intent deleteIntent = new Intent(mContext, AlarmReceiver.class);
deleteIntent.setAction(TripService.ACTION_CANCEL);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
deleteIntent.setDataAndTypeAndNormalize(alertUri, TripService.REMINDER_MIME_TYPE);
} else {
deleteIntent.setDataAndType(alertUri, TripService.REMINDER_MIME_TYPE);
}
return new NotificationCompat.Builder(mContext)
.setSmallIcon(R.drawable.ic_stat_notification)
.setDefaults(Notification.DEFAULT_ALL)
.setOnlyAlertOnce(true)
.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
deleteIntent, 0))
//.setLights(0xFF00FF00, 1000, 1000)
//.setVibrate(VIBRATE_PATTERN)
.build();
REMINDER_MIME_TYPE is application/vnd.com.joulespersecond.seattlebusbot.reminder
Manifest for using the MIME type:
<receiver android:name=".AlarmReceiver">
<!-- These action names must match the constants in TripService -->
<intent-filter>
<action android:name="com.joulespersecond.seattlebusbot.action.SCHEDULE" />
<action android:name="com.joulespersecond.seattlebusbot.action.POLL" />
<action android:name="com.joulespersecond.seattlebusbot.action.CANCEL" />
<data android:mimeType="application/vnd.com.joulespersecond.seattlebusbot.reminder" />
</intent-filter>
</receiver>
I also tried not using the support library (i.e., using Notification.Builder instead of NotificationCompat.Builder), but that didn't change anything either.
Any ideas why this isn't working on Android 4.4.3/4.4.4?
More info is shown in the Github issue for this problem.
EDIT
I've also replicated this issue in a small Github project "DeleteIntentDemo":
https://github.com/barbeau/DeleteIntentDemo
Instructions to reproduce are in the README for this project.
EDIT 2
This appears to be due to a bug in Android in Notification.setLatestEventInfo() - I've reported it here:
https://code.google.com/p/android/issues/detail?id=73720
Please see #CommonsWare's answer for the workaround.
EDIT 3
My AOSP patch to fix this issue has now been merged so this problem won't appear for legacy apps in future releases of Android:
https://code.google.com/p/android/issues/detail?id=73720#c4
However, in the above AOSP thread is it emphasized that one should no longer be using Notification.setLatestEventInfo() - instead, use Notification.Builder to create a new Notification.
In your sample project, if you remove the following line, the deleteIntent works on a Nexus 4 running 4.4.4:
setLatestInfo(getActivity(), notification, routeId);
I suspect that this call is wiping out your deleteIntent. It may work to re-apply your deleteIntent to the Notification as part of your setLatestInfo() processing.
You must have a different problem because I'm able to receive the deleteintent in several 4.3 and 4.4 emulators.
I wanted to test your "simple" project but it uses Android Studio, so I made my own simpler test.
Steps to reproduce:
-Create an Activity and set the launch mode to singleInstance in the manifest.
-In the handler of a button or menu item, launch a notification:
Intent deleteIntent = new Intent(this, MainActivity.class);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setOnlyAlertOnce(true)
.setContentTitle("Notification delete intent test")
.setContentText("Please dismiss this notification by swipping or deleting it. A Toast will be shown if the deletion intent works.")
.setDeleteIntent(PendingIntent.getActivity(this, 0, deleteIntent, 0))
.setAutoCancel(true)
.build();
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.notify((int)System.currentTimeMillis(), notification);
-Override onNewIntent to show a toast or log a message when the notification is cancelled:
#Override
public void onNewIntent(Intent intent){
Toast.makeText(this, "Notification deleted!", Toast.LENGTH_LONG).show();
}
To dismiss the notification either swipe or press the clear button. It wont work pressing over it because autocancel is not considered an explicit user action and hence the delete intent wont be delivered.

Android - Set notification to never go away

I have this code that works great:
Notification n = builder.build();
n.flags = Notification.FLAG_NO_CLEAR;
But when I restart the phone, the notification goes away. Is there any flag that make that happen?
If you want to print notification when the device boots up, you can create a receiver that is invoked when the system boot is completed, for this, first create a receiver,
public class MyReciever extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent)
{
Log.d("BOOT COMPLETE","SERVICE CALLED>>>>>>>>>>>>");
//use your code here to print notifications
}
}
This receiver is invoked when the system boot is completed. You can also call a service from the onReceive method of receiver to print the notification.
Also you must define the following regularities in your manifest file,
First define permission for getting BOOT_COMPLETION intent,
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Then define your receiver also,
<receiver android:name=".MyReciever"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
No. I don't think that is possible.
You could have a service that runs at start-up to to bring up that notification again. Notifications otherwise do not persist across reboots.

How to make alarm ring to the set time event after device is switched off and switched on?

I've created alarm for each event in calendar while created. It works fine. Now if i set an event for 6pm and if i switch off and switch on the device, the alarm is ringing suddenly when the device is switched on. the alarm is not ringing for the event set time. My code for OnBootReceiver as follows:
#Override
public void onReceive(Context context, Intent intent)
{
AlarmManager mgr=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i=new Intent(context, AlarmReceiver.class);
PendingIntent pi=PendingIntent.getBroadcast(context, ap.id1,i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime(),PERIOD,pi);
}
First of all save that timings in one databse
call a broad cast reveiver class like below
use below code in manifest file
<receiver android:name=".main.SampleOnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
in above code SampleOnBootReceiver is acalss which extends from BroadcastReceiver.
in that class u take alaram time from saved databse and set the alaram

How can I access an Activity from another class globally in an Android app?

I have an Android app with 2 activities defined below. In the MainMenu.oncreate(), I have an AlarmManager kicked off to periodically query a server for data and update the text of a button in the PlayBack UI. Can I access the Playback object via a global reference or do I need to kick off the AlarmManager in the Playback.oncreate() instead so I can pass a reference to it? If so, should this be done with a BroadcastReceiver and Intent as I'm doing in the MainMenu shown below?
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".MainMenu"
android:label="#string/app_name">
</activity>
<activity android:name=".Playing" android:label="#string/playing_title">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".NotificationUpdateReceiver" android:process=":remote" />
<service android:name="org.chirpradio.mobile.PlaybackService"
public class MainMenu extends Activity implements OnClickListener {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_menu);
View playingButton = findViewById(R.id.playing_button);
playingButton.setOnClickListener(this);
try {
Long firstTime = SystemClock.elapsedRealtime();
// create an intent that will call NotificationUpdateReceiver
Intent intent = new Intent(this, NotificationUpdateReceiver.class);
// create the event if it does not exist
PendingIntent sender = PendingIntent.getBroadcast(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// call the receiver every 10 seconds
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME, firstTime, 10000, sender);
} catch (Exception e) {
Log.e("MainMenu", e.toString());
}
}
}
I have an Android app with 2 activities defined below.
You only have one activity.
In the MainMenu.oncreate(), I have an AlarmManager kicked off to periodically query a server for data and update the text of a button in the PlayBack UI.
Why? Do you intend for these alarms to go on even after the user exits out of the activity?
Can I access the Playback object via a global reference or do I need to kick off the AlarmManager in the Playback.oncreate() instead so I can pass a reference to it?
Neither.
Using AlarmManager means that you want the periodic work to continue even after the user exits the activity. Hence, it is very likely that there is no "Playback object", since the user probably is not in your activity. Your service can send its own broadcast Intent to be picked up if the Playback activity is still around. This sample project demonstrates using an ordered broadcast for this, so that if the activity is not around, a Notification is raised instead.
If, on the other hand, you do not want the periodic work to continue if the user gets out of the activity, then do not use AlarmManager. Use postDelayed() within the activity, using a Runnable that triggers your service via startService(), then reschedules itself via postDelayed(). In this case, you can consider using something like a Messenger as a way to have the service let the activity know what is going on, if the activity is still around. This sample project demonstrates the use of a Messenger in this fashion.

Categories