I'm very inexperienced with Android and having trouble trying to get an AlarmManager to basically run a small activity that spawns a notification every hour (for testing purposes I lowered it to every 10 seconds.
However it doesn't seem like the activity gets called at all. Logcat shows nothing and no Toast appears, let alone notifications being created.
Context may be the issue, I didn't entirely understand what I was meant to be passing there so I might have made a mistake by passing in MainActivity.this but I couldn't figure out what would be more appropriate when I tried to Google it.
This code is contained in my MainActivity's OnCreate method
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
task = enterTask.getText().toString();
if (!task.equals("Enter task here...") &&
!task.equals("")) {
Toast.makeText(getApplicationContext(),
"Saved task " + task,
Toast.LENGTH_LONG).show();
SharedPreferences.Editor editor = prefs.edit();
editor.putString("Current_Task", task);
editor.commit();
Context context = MainActivity.this;
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, Notify.class);
PendingIntent pi=PendingIntent.getBroadcast(context, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
//AlarmManager.INTERVAL_HOUR,
10000,
pi);
}
}
});
And this is my Notify activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_notify);
Toast.makeText(getApplicationContext(),
"Notifying",
Toast.LENGTH_LONG).show();
prefs = PreferenceManager.getDefaultSharedPreferences(this);
try {
task = prefs.getString("Current_Task", "");
} catch (Exception e) {
e.printStackTrace();
}
if (!task.equals("")){
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Current task")
.setContentText("Hey! Have you " + task + "?");
mNotificationManager.notify(1, mBuilder.build());
}
}
Can anyone help me understand my mistake?
for testing purposes I lowered it to every 10 seconds
Note that depending on your Android version, it may not occur that frequently. Starting with Android 5.1, the minimum period is one minute.
However it doesn't seem like the activity gets called at all.
Starting an activity periodically via AlarmManager is akin to shooting your users in the face with a shotgun: yes, it gets their attention, but not a very nice way.
In fact, your code to set up the alarms has it right: you are using getBroadcast() for the PendingIntent. This will trigger a BroadcastReceiver, not an Activity. Using a BroadcastReceiver is a far better solution for raising your Notification, as the BroadcastReceiver itself does not have any UI and therefore will not interrupt the user by taking over the foreground.
I recommend that you set up a manifest-registered BroadcastReceiver and put your code from onCreate() (from the preferences onwards) into onReceive() of the BroadcastReceiver.
If you really want to start an activity periodically, and you don't mind your users threatening to do unfortunate things to you, use getActivity() instead of getBroadcast() when creating your PendingIntent.
Related
I have a problem on my app and I want to report this bug.
I develope the app which can crawls notifications using NotificationListenerService.
It works well.
But NotificationListenerService class has the problem I think.
Because, If the app is crashed, app can't crawl the notification at all,
UNTIL the phone reboots.
Is anyone who can solve this problem??
Please help me.
The bug is very clear!! But It is not easy to find the solution ....
If do you have already permissions then:
In your service class or another service/activity you can switch the "component hability" to listen notifications:
public void tryReconnectService() {
toggleNotificationListenerService();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ComponentName componentName =
new ComponentName(getApplicationContext(), NotificationReaderV2Service.class);
//It say to Notification Manager RE-BIND your service to listen notifications again inmediatelly!
requestRebind(componentName);
}
}
/**
* Try deactivate/activate your component service
*/
private void toggleNotificationListenerService() {
PackageManager pm = getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(this, NotificationReaderV2Service.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(new ComponentName(this, NotificationReaderV2Service.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
Your notification listener, is a SERVICE, it can be killed by System, you can do your service as FOREGROUND to drastically decrease the probability that the system will kill your service.
#Override
public void onListenerConnected() {
super.onListenerConnected();
Log.d(TAG, "Service Reader Connected");
Notification not = createNotification();
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
mNotificationManager.notify(NOTIFICATION_ID, not);
}
startForeground(NOTIFICATION_ID, not);
//Alarm to auto - send Intents to Service to reconnect, you can ommit next line.
alarmIt();
}
If do you like so more "safe", you can to programming not-friendly battery alarms, try to use inexact alarms please, the user's battery will be happy:
private void alarmIt() {
Log.d(TAG, "ALARM PROGRAMMATED at"+HotUtils.formatDate(new Date()));
Calendar now = Calendar.getInstance();
now.setTimeInMillis(System.currentTimeMillis());
now.set(Calendar.MINUTE, now.get(Calendar.MINUTE) + 1);
Intent intent = new Intent(this, NotificationReaderV2Service.class);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setAction(REBIND_ACTION);
PendingIntent pendingIntent = PendingIntent.getService(this, 0,
intent, 0);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//The alarms that are repeated are inaccurate by default, use RTC_WAKE_UP at your convenience.
//Alarm will fire every minute, CHANGE THIS iF DO YOU CAN, you can't use less than 1 minute to repeating alarms.
manager.setRepeating(AlarmManager.RTC_WAKEUP, now.getTimeInMillis(), 1000 * 60 * 1, pendingIntent);
}
and next read the Intent to reconnect service binding:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Notification service onStartCommandCalled");
if (intent!=null && !HotUtils.isNullOrEmpty(intent.getAction()) && intent.getAction().equals(REBIND_ACTION)){
Log.d(TAG, "TRYING REBIND SERVICE at "+HotUtils.formatDate(new Date()));
tryReconnectService();//switch on/off component and rebind
}
//START_STICKY to order the system to restart your service as soon as possible when it was killed.
return START_STICKY;
}
Keep in mind that doing all these steps you can sure that your service will be killed anyway by the system but this code will restart the service and make it harder to kill it.
Maybe, you should consider using PARTIAL_WAKE_LOCK with your service and execute it in a process independently (:remote) if you want even more certainty (Maybe this is useless)
I would like to add a common error that is often followed, NEVER override the onBind and onUnbind method or overwrite the INTENT ACTION.
This will cause your service to not be connected and never run onListenerConnected
Keep the Intent as it is, in most cases you do not need to edit it.
I see exactly the same on this. The only "solution" I've found was to have the notification listener running in a separate process. Then if the rest of the app crashes it doesn't stop the listener. So it's only then specifically notification listener service crashes that require the reboot.
Seems a terrible and over complicated solution though.
I had the same problem. Here are few things that I did and now it works wonderfully for me.
Override onStartCommand, call super and return START_STICKY;
Override onNotificationRemoved, call super and add a toast so that you know in android itself that you service has not died yet whenever you swipe a notification.
Exclude your app from Battery saving list (Settings-> Battery-> Power Saving Exclusion)
Post this the service never dies even after the main app's crash. I dont need to reboot now to restart it.
I just started making an application aside from school and work and am not sure what route I should take. Essentially, I am building an app that calculates 5 different prayer times that change every day (the 5 prayers are named Fajr, Zuhr, Asr, Maghrib, and Isha). The calculation is done locally on the device and I found open source code for that and got it to calculate them properly. Once the getTimes() method is called, the prayer times should be calculated for that day and then recalculated once every single day after that. I'm thinking the setRepeating() method of the AlarmManager class would be good for that. How would I go about that? Once the prayer times are calculated, a service(s) should be started to create a notification at that exact time to notify the user that it is time to pray. The dilemma here is that I don't think that I should be using 5 different services/receivers to notify for each of the 5 different prayers. What would be the best way to go about this?
Currently, my app only notifies the user of Maghrib (one of the prayers) prayer time. It does not recalculate the times either.
Sorry if I am not very clear as I am new to this. I can expand more if needed.
My getTimes() method: (for the sake of simplicity I have removed the code that calculates the times)
public void getLocationTime(View v) {
//Maghrib
Calendar calMaghribTime = Calendar.getInstance();
calMaghribTime.set(Calendar.HOUR_OF_DAY, getHourOfDay(strMaghribTime));
calMaghribTime.set(Calendar.MINUTE, Integer.parseInt(strMaghribTime.substring(3,5)));
calMaghribTime.set(Calendar.SECOND, 0);
Intent myIntent = new Intent(MainActivity.this, NotificationCreatorReceiver.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, calMaghribTime.getTimeInMillis(), pendingIntent);
Toast.makeText(this, "NotificationCreator onReceive()", Toast.LENGTH_SHORT).show();
} //end of getLocationTime()
Here is my receiver:
public class NotificationCreatorReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service1 = new Intent(context, NotificationCreatorService.class);
context.startService(service1);
}
}
Here is my service:
public class NotificationCreatorService extends Service {
#Override
public IBinder onBind(Intent arg0)
{
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate()
{
super.onCreate();
// TODO Auto-generated method stub
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "NotificationCreator onStartCommand()", Toast.LENGTH_SHORT).show();
// Use NotificationCompat.Builder to set up our notification.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
//icon appears in device notification bar and right hand corner of notification
builder.setSmallIcon(R.mipmap.ic_launcher);
// This intent is fired when notification is clicked
Intent intent1 = new Intent(this.getApplicationContext(),MainActivity.class);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity(this.getApplicationContext(),
0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
// Set the intent that will fire when the user taps the notification.
builder.setContentIntent(pendingNotificationIntent);
// Content title, which appears in large type at the top of the notification
builder.setContentTitle("It's time for Maghrib");
// Content text, which appears in smaller text below the title
builder.setContentText("Maghrib prayer time has started in your area");
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Will display the notification in the notification bar
notificationManager.notify(0, builder.build());
return START_STICKY;
}
#Override
public void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
}
}
The short answer - probably 1 service, 5 receivers. A Receiver is designed to listen for events and take quick action. A Service should be used to do all of the "heavy lifting" if necessary.
A Receiver should listen for an event and, if you only need to post a notification, then you can probably just do that and be done. But if you want to do much of anything else, it should pass an Intent to the Service with data to tell the Service how to respond.
EDIT:
Receivers have 10 seconds to do their job or else an ANR (Application Not Responding) error will occur. (See the docs: http://developer.android.com/reference/android/content/BroadcastReceiver.html) Creating and sending a notification should not take this long.
However, "good design" means you should acquire a Wake Lock and then pass an intent to a Service to do much of anything. Also, you will probably find that you will want to do "other processing" at some point. However, if I were you and all that is required is to post a notification, I'd just use the Receiver and worry about it later. I've probably processed over a billion notifications this way without error. But a code reviewer may argue that it's "possible" for an ANR to occur... blah... blah... blah...
I have a problem with running a service from Alarm manager.
I am building an app that notifies the owner on the namedays of his facebook friends. It all works nicely, but the notification won't show up.
I've set up an AlarmTask that creates the PendingIntent and sets the AlarmManager, like this:
public void run() {
// Request to start are service when the alarm date is upon us
Intent intent = new Intent(context, NotifyService.class);
intent.putExtra(NotifyService.INTENT_NOTIFY, true);
intent.putExtra("notifyID", ID);
PendingIntent pendingIntent = PendingIntent.getService(context, ID, intent, 0);
// Sets an alarm - note this alarm will be lost if the phone is turned off and on again
am.set(AlarmManager.RTC_WAKEUP, date.getTimeInMillis(), pendingIntent);
}
The ID is specific for every nameday.
Now in my NotifyService, I have set up these:
#Override
public void onCreate() {
super.onCreate();
System.out.println("NOTIFICATION SERVICE onCreate()");
mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
System.out.println("INTENT RECIEVED: " + intent + " " + flags + " " + startId);
// If this service was started by out AlarmTask intent then we want to show our notification
if(intent.getBooleanExtra(INTENT_NOTIFY, false)){
int ID = intent.getIntExtra("notifyID", -1);
showNotification(ID);
}
// We don't care if this service is stopped as we have already delivered our notification
return START_STICKY;
}
Both the methods are executed once when I start the app, but when the notification should come up, nothing happens.
Is there a way to test if the AlarmManager really executes the PendingIntent?
Should I rather use IntentService? Why/how?
Thanks a lot.
I tried to change it to BroadcastReciever, looking like this:
public class NotificationBroadcastReciever extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("BROADCAST RECIEVED");
}
}
The AlarmTask bit is changed to this:
Intent intent = new Intent("NotificationBroadcast");
intent.putExtra(NotifyService.INTENT_NOTIFY, true);
intent.putExtra("notifyID", ID);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), ID, intent, 0);
System.out.println("date for notification: " + date.get(Calendar.DAY_OF_MONTH) + "." + date.get(Calendar.MONTH) + "." + date.get(Calendar.YEAR));
System.out.println("epoch time in milils: " + date.getTimeInMillis());
// Sets an alarm - note this alarm will be lost if the phone is turned off and on again
am.set(AlarmManager.RTC_WAKEUP, date.getTimeInMillis(), pendingIntent);
and relevant manifest part looks like this:
<receiver
android:name="cz.cvut.kubispe2.jmeniny.NotificationBroadcastReciever"
android:exported="false">
<intent-filter>
<action android:name="NotificationBroadcast" />
</intent-filter>
</receiver>
I checked if the date that is to be set is equal to the epoch time and it is, but still, the onRecieve method is never called.
Both the methods are executed once when I start the app, but when the notification should come up, nothing happens.
_WAKEUP alarms are only guaranteed to wake up the device if they route to a BroadcastReceiver, not a Service. So long as what you are doing is very short (1-2 milliseconds), you can safely do that work in onReceive() of a BroadcastReceiver. The work you are presently doing in your Service would qualify.
Beyond that, use adb shell dumpsys alarm to confirm that your alarm is scheduled for when you think it is.
Should I rather use IntentService?
It would certainly be a better option than a regular Service, which you are leaking in your current implementation. However, the _WAKEUP limitation still holds, which is why I wrote WakefulIntentService, to help bridge the gap. Again, though, with the current limited work you are doing, just using a BroadcastReceiver should suffice.
try using application context.
PendingIntent pendingIntent = PendingIntent.getService(context.getApplicationContext(), ID, intent, 0);
And work with android logs. Then you will see if it's running in your console
Seems like I finally resolved it, I used the broadcast reciever, and found out where the error was - Calendar takes the month argument from 0 to 11, instead of 1-12, which I thought, since all the other arguments are dealt with normally. So I was just putting up a notification for the end of May, instead of today, when testing.
Anyway, thank you all for help, it was very appreciated.
Maybe the question is obvious, but I would like to be sure based on what my app does.
I am writing a GPS tracker. I use the location services inside a static class to get my positions. Everything works fine. However I have noticed that from time to time my app gets killed when it has been running for a couple of hours. In order to resolve that I thought in converting it to a foreground service.
Instead of rewriting my code and put all my logic inside the service, I thought in just creating a foreground service that does nothing. I will start this service when the user decides to start tracking. Remember that I use the location services inside a static class.
Will this be safe or the OS will find out that this service is not doing anything at all and it will have more chances to be killed?
Here is my service:
public class SimpleService extends Service
{
private static final int ONGOING_NOTIFICATION = 1;
#Override
public void onCreate()
{
super.onCreate();
Notification notification = new Notification(R.drawable.icon, "Tracking started", System.currentTimeMillis());
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, "notification title", "notification message", pendingIntent);
startForeground(ONGOING_NOTIFICATION, notification);
}
#Override
public void onDestroy()
{
super.onDestroy();
stopForeground(true);
}
}
Thanks
I did a similar thing for a service I created that ran periodically to download a file if it changed but found it was being killed by the OS after several hours. Since using startForeground, the problem has gone away.
I don't think there'd be a difference between my service that does minimal work and your empty one.
I am still working on my location-based alarm android application. I have an AlarmService class that starts notifications and proximity alerts. I am starting this service with:
startService(intentAlarmService);
I try to stop the service using:
Intent intentAlarmService = new Intent(this, AlarmService.class);
stopService(intentAlarmService);
This is what happens: The service stops, but then, when I start another instance of the service (i.e. exit the app, launch the app, start the service) - I discover (through Toasts) that the previous instances of the service are still running. For example, in the AlarmService class, there is a LocationListener with the onLocationChanged method. So, in this method, I put:
Toast.makeText(AlarmService.this, "AlarmTitle: " + mAlarmTitle, Toast.LENGTH_SHORT).show();
And when I re-start the service, Toasts keep showing up with the previous AlarmTitles, and the current AlarmTitle.
So, something is not working when I try to stop the AlarmService - what could this be?
Note: when I re-install the application, the service stops for real. Then when I start the service, only the current AlarmTitle shows in the Toast (I want this to happen every time).
Something is wrong with my service. Any ideas what I can do?
thanks.
CODE FROM MY APP:
public void onDestroy() {
super.onDestroy();
Intent alarmIntent = new Intent(getApplicationContext(), AlarmReceiver.class);
PendingIntent pendingIntentAlarm = PendingIntent.getBroadcast(getApplicationContext(), PENDING_INTENT_REQUEST_CODE1, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
pendingIntentAlarm.cancel();
Intent intentAlarmService = new Intent(getApplicationContext(), AlarmService.class);
stopService(intentAlarmService);
mNtf.cancel(NOTIFICATION_ID1);
mNtf.cancelAll();
}
I discover (through Toasts) that the previous instances of the service are still running.
My guess is that you are leaking services, probably by failing to call removeUpdates() to detach your LocationListener. There is only one true running copy of the service (from an Android lifecycle standpoint), but you are preventing the other services from being garbage collected through your leak.
Also, please replace all occurrences of getApplicationContext() with this.
test this :
private Boolean Tb = true;
if(condition)
{
if(Tb)
{
Toast.makeText(getApplicationContext(),"content...", Toast.LENGTH_LONG).show();
}
Tb =false;
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Tb =true;
}
}, Toast.LENGTH_LONG);
}
}