What I want
I want a notification every day at the same time.
I already read some posts and tutorials/examples but it won't work correctly.
Version 1
The Error: Android process / service dies every ~3 minutes after re/starting
11-07 07:33:05.725 4611 6121 I ActivityManager: Process at.htl3r.appmosphere (pid 5238) has died.
11-07 07:33:05.725 4611 6121 W ActivityManager: Scheduling restart of crashed service at.htl3r.appmosphere/.notify.NotifyService in 14648ms
11-07 07:33:20.400 4611 4632 I ActivityManager: Start proc at.htl3r.appmosphere for service at.htl3r.appmosphere/.notify.NotifyService: pid=5463 uid=10096 gids={50096}
---
11-07 07:33:41.580 4611 4623 I ActivityManager: Process at.htl3r.appmosphere (pid 5463) has died.
11-07 07:33:41.580 4611 4623 W ActivityManager: Scheduling restart of crashed service at.htl3r.appmosphere/.notify.NotifyService in 73293ms
11-07 07:33:44.310 4611 5385 F ProcessStats: Starting service ServiceState{43760cf0 at.htl3r.appmosphere.notify.NotifyService pkg=at.htl3r.appmosphere proc=43760cf0} without owner
these are the two ways (with and without owner in last line)
This bug is only on my S3 so extrem, on my N7 (2013) is it a bit better
After every restart I get a notification. (just a thought: And if I delete it, the possibility is higher to make a crash.)
A bit annoying to receive a notification every 3 minutes ^-^
The Code
version 1 - with service
UPDATE 1
updated code like Larry Schiefer told
new full log
UPDATE 2
NotifyManager
see below for newest version
version from this update
NotifyReceiver
public class NotifyReceiver extends BroadcastReceiver {
private static final String TAG = "NotifyReceiver";
public static final int ID_NEWHINTAVAILABLE = 1;
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive");
SharedPreferences spref = PreferenceManager.getDefaultSharedPreferences(context);
NotificationManager mNM = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent i = new Intent(context.getApplicationContext(), MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, i, 0);
Notification.Builder mNotifyBuilder = new Notification.Builder(context);
mNotifyBuilder.setSmallIcon(R.drawable.ic_stat_name);
mNotifyBuilder.setContentTitle(context.getString(R.string.app_name));
mNotifyBuilder.setContentText(context.getString(R.string.notification_contenttext));
mNotifyBuilder.setContentIntent(pIntent);
mNotifyBuilder.setAutoCancel(true);
// has to have an icon - now the app icon
// auto cancel after click: in main use cancel(int id);
// mNotifyBuilder.addAction(R.drawable.ic_stat_name, getString(R.string.notification_action), pIntent);
// mNotifyBuilder.setTicker(getString(R.string.app_name));
// mNotifyBuilder.setTicker(getString(R.string.app_name)+" "+getString(R.string.notification_contenttext));
// mNotifyBuilder.setWhen(System.currentTimeMillis());
// mNotifyBuilder.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS);
// http://stackoverflow.com/questions/2724871/how-to-bring-up-list-of-available-notification-sounds-on-android
String sound = spref.getString(SettingsFragment.pref_notify_sound, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).toString());
mNotifyBuilder.setSound(Uri.parse(sound));
if (spref.getBoolean(SettingsFragment.pref_notify_vibrate, true)) {
// mNotifyBuilder.setVibrate(new long[] { 0, 1000 });
mNotifyBuilder.setDefaults(Notification.DEFAULT_VIBRATE);
}
if (spref.getBoolean(SettingsFragment.pref_notify_light, true)) {
mNotifyBuilder.setLights(Color.GREEN, 3000, 3000);
}
Notification mNotify = mNotifyBuilder.build();
mNM.notify(ID_NEWHINTAVAILABLE, mNotify);
NotifyManager.startAlarm(context, true);
// wenn aktiviert: ausgeführt & neu gestartet
// bei Deaktiviertung: abgebrochen - demnach kein Neustart
}
}
Update 3
Autostart worked..
but now, it dies too
nothing changed in this code; only the code above
<receiver android:name="at.htl3r.appmosphere.notify.Autostart" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Autostart.java
public class Autostart extends BroadcastReceiver {
private static final String TAG = "autostart";
#Override
public void onReceive(Context context, Intent intent) {
if (NotifyManager.isNotificationEnabled(context)) {
NotifyManager.startAlarm(context);
Log.i(TAG, "started");
}
}
}
CatLog
s3 - full
n7
12-14 23:15:19.227 1452 1679 I ActivityManager: Start proc at.htl3r.appmosphere for broadcast at.htl3r.appmosphere/.notify.Autostart: pid=5837 uid=10391 gids={50391, 3003}
12-14 23:15:42.300 1452 4109 I ActivityManager: Killing 5837:at.htl3r.appmosphere/u0a391 (adj 15): empty #17
12-15 06:43:47.501 18799 18819 D JsonParser: at.htl3r.appmosphere: publishState=6
12-15 06:43:47.501 18799 18819 D JsonParser: Skipping app 0 with state != 1: package name=at.htl3r.appmosphere: state=6
Update 4
NotifyManager
public class NotifyManager {
private static final String TAG = "NotifyManager";
/**
* {#link #startAlarm(Context, boolean)}<br>
* default: restart: true
*
* #param context Context of activity
* #return alarm started: true<br>
* is running: false
*/
public static boolean startAlarm(Context context) {
return startAlarm(context, false);
}
/**
* #param context Context of activity
* #param restart start the alarm even when already running
* #return true if started | false if running and not started
*/
public static boolean startAlarm(Context context, boolean restart) {// todo restart alarm on settings change
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
SharedPreferences spref = PreferenceManager.getDefaultSharedPreferences(context);
String time = spref.getString(SettingsFragment.pref_notify_time, TimePreference.notify_default);
int hour = Integer.parseInt(time.split("\\:")[0]);
int minute = Integer.parseInt(time.split("\\:")[1]);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.HOUR_OF_DAY, hour);
// alternative: HOUR and AM_PM
if (calendar.getTimeInMillis() < Calendar.getInstance().getTimeInMillis()) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
// String time = new SimpleDateFormat("hh:mm", Locale.getDefault()).format(calendar.getTime());
if (!isAlarmRunning(context) || restart) {
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), getPendingIntent(context));
Log.d(TAG, "Start Alarm at " + time);
// Toast.makeText(context, "Start Alarm at " + time, Toast.LENGTH_LONG).show();
return true;
}
Log.d(TAG, "Service already running");
return false;
}
/**
* #param context Context of activity
* #return true if running and canceled
*/
public static boolean cancelAlarm(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (isAlarmRunning(context)) {
alarmManager.cancel(getPendingIntent(context));
Log.d(TAG, "Cancel Alarm");
NotifyManager.isAlarmRunning(context);
// Toast.makeText(context, "Cancel Alarm from " + time, Toast.LENGTH_LONG).show();
return true;
}
Log.d(TAG, "Service already canceled");
return false;
}
/**
* #param context Context of activity
* #return if alarm is running
*/
public static boolean isAlarmRunning(Context context) {
Intent intent_service = new Intent(context, NotifyReceiver.class);
Log.d(TAG, "isAlarmRunning:" + (PendingIntent.getBroadcast(context, 0, intent_service, PendingIntent.FLAG_NO_CREATE) != null));
return (PendingIntent.getBroadcast(context, 0, intent_service, PendingIntent.FLAG_NO_CREATE) != null);
}
/**
* #param context Context of activity
* #return PendingIntent
*/
public static PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, NotifyReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
// If it exists return it
if (pi != null)
return pi;
// It doesn't exist, make it (last parameter to 0 for reusable):
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
/**
* #return yyMMdd
*/
public static String getCurrentTimeStamp() {
SimpleDateFormat sdfDate = new SimpleDateFormat("yyMMdd", Locale.getDefault());
Date now = new Date();
String strDate = sdfDate.format(now);
return strDate;
}
/**
* #param context Context of the activity
* #return if notification is enabled or not
*/
public static boolean isNotificationEnabled(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.pref_notify, true);
}
}
Point A: The service code is missing a key component
In the code above, the service has an onCreate and onDestroy, which will be triggered when the service is created and destroyed. However, if a service is triggered and it is already running, then it will not go through onCreate. It will, however, go through onstartCommand (onStart pre android 2.0). The actual structure of your code should be:
onCreate() {
// Stuff you only do when this class is instantiated the first time
// and don't need to do if it is called (started in android terminology)
// thereafter
}
// The next two are >=2.0 and then <2.0
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startHandleIntent(intent);
return START_STICKY; // If you want the service to hang around
}
#Override
public void onStart(Intent intent, int startId) {
startHandleIntent(intent);
}
void startHandleIntent(Intent intent) {
// Do things that shiould happen every time here
// eg. in your case, the notification
}
Point B: This isn't really what a service was designed for
You cannot rely on a service hanging around for that long. Inactive services will often be removed to make space for other things. Given that the the service does very little, it would probably be better to use a BroadcastReceiver, which was designed specifically for things that need triggering occasionally but don't really need to be there otherwise. So:
Use a BroadcastRecevier to catch the triggers and issue a notification. Something like this:
class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Issue the notidfication
<...>
// Reissue a request for a future alarm call here if needed
<...>
}
}
Remember to set it up to receive broadcasts in the manifest:
<application>
... other stuff ...
<receiver android:name=".MyBroadcastReceiver" android:enabled="true">
<intent-filter>
<action android:name="com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION"/>
</intent-filter>
</receiver>
</application>
To trigger that, you need an intent that will trigger a broadcast:
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
context.sendBroadcast(intent);
If you are setting it up to call later via a PendingIntent (change the final flag to zero if you want a reusable PendingIntent for a recurring event):
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT)
If later on you wish to change, or cancel somehting, or if you simply need to know if the Pending Intent exists from the system's point of view:
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
// It exists. If you want then to cancel the alarm that triggers it:
alarmManager.cancel(pi);
}
else {
// It doesn't exist. If you need to create a reusable PendingIntent:
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
}
Personally, I would use this approach instead of initializePendingIntent, ie:
public static PendingIntent getPendingIntent() {
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
// If it exists return it
if (pi != null) return pi;
// It doesn't exist, make it (last parameter to 0 for reusable):
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
Use SharedPreferences (as you already do) to keep track of what is going on (time of alarm)
My preference would be to only create a one shot alarm with a one shot intent for when the next alarm should sound. If it changes, remove this alarm and create a new one. When it triggers, crate a new one. This way you minimise the number of things that have to stay alive for lengths of time.
Check your logcat for a stack trace. It will be before the activity manager service entries you have provided. This line looks suspect to me, specifically the setAction as it is not providing a proper resource value for the icon:
mNotifyBuilder.setContentTitle(getString(R.string.app_name)).setContentText(getString(R.string.notification_contenttext)).setContentIntent(pIntent).addAction(0, getString(R.string.notification_action), pIntent).setAutoCancel(true)
Related
So I made an app that upon a button click sets up a repeating task using an Alarm Manager.
In on create:
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
servicePendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
On the button click:
alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
firingCal= Calendar.getInstance();
firingCal.setTimeInMillis(System.currentTimeMillis());
firingCal.set(Calendar.HOUR_OF_DAY, 1); // At the hour you want to fire the alarm
firingCal.set(Calendar.MINUTE, 47); // alarm minute
firingCal.set(Calendar.SECOND, 0); // and alarm second
long intendedTime = firingCal.getTimeInMillis();
long interval = 1000 * 60 * 1;
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, intendedTime, interval, servicePendingIntent);
In the AlarmReceiver class:
public void onReceive(Context context, Intent intent) {
Intent myIntent = new Intent(context, WallpaperService.class);
context.startService(myIntent);
Log.d(TAG,"Am apelat serviciul");
context.stopService(myIntent);
}
And in the WallpaperService class I just make a get request and set an wallpaper.
public class WallpaperService extends Service {
String requestLink="";
boolean requestFinished = false;
public final String TAG = "Service";
public static int SERVICE_ID = 1;
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"Wallpaper Service started");
Toast.makeText(WallpaperService.this,"Service started",Toast.LENGTH_LONG);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"In onStartCommand");
taskToBeRepeated();
stopSelf();
return START_STICKY;
}
.....
}
And the behaviour is that when I start the app and I click the button everything works well the first time the Alarm Manager fires ( With the app in the background). The second time the receiver gets triggered I get the error in the tile. To be more specific :
java.lang.RuntimeException: Unable to start receiver com.example.dailywallpaper.AlarmReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.example.dailywallpaper/.WallpaperService }: app is in background uid UidRecord{3e313bf u0a357 RCVR bg:+1m21s273ms idle change:uncached procs:1 seq(0,0,0)}
What seems to be the problem ? And why is working the first time and then it gives the error? How can I fix it ?
you need to read android official documentation about the policy of using background service or alarms in android 8 and above and adapt your app with this limitations.
I suggest you to read this two articles very carefully :
https://developer.android.com/guide/components/services
https://developer.android.com/about/versions/oreo/background
I'm trying to set an alarm to fire every 5 minutes.
This is the code for setting the alarm :
#Override
public void scheduleAlarmManager() {
Timber.i("After SignIn sets AlarmManager");
// broadcast
Intent intent = new Intent(this, PatientAlarmReceiver.class);
intent.setAction(PATIENT_START_ALARM_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, REQUEST_CODE, intent, 0);
// and set alarmManager
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Calendar currentCal = Calendar.getInstance();
long currentTIme = currentCal.getTimeInMillis();
// if there's not an Alarm already set then set one
if (!isAlarmSet(this)) {
Timber.i("Alarm not set - so set one");
alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
currentTIme + TWO_MINS_DURATION, TWO_MINS_DURATION, pendingIntent);
}
}
and I can verify that I set the alarm correctly since I see in my logcat the messages I log with Timber.
My Receiver class is :
public class PatientAlarmReceiver extends BroadcastReceiver {
public static final String TAG = "PATIENT-ALARM-RECEIVER";
public static final String PATIENT_START_ALARM_ACTION = "bp.headsup.receivers.alarm.patient";
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Inside OnReceive Patient");
Timber.i("Inside OnReceive Patient");
if (intent == null || intent.getAction() == null) {
return;
}
String action = intent.getAction();
if (PATIENT_START_ALARM_ACTION.equalsIgnoreCase(action)) {
onStartCheckForConnectionRequest(context);
}
}
/**
* If is connected to network starts services
*/
private void onStartCheckForConnectionRequest(Context context) {
NetworkUtils networkUtils = new NetworkUtils(context);
if (networkUtils.isNetworkConnected()) {
Intent checkForConnRequestIntent = new Intent(context, PatientCheckForConnectionRequestService.class);
context.startService(checkForConnRequestIntent);
Timber.i("Starts Service From PatientALARMMANAGER");
}
}
}
And I have declared in Manifest :
<!-- Receivers -->
<receiver
android:name="bp.headsup.receivers.PatientAlarmReceiver" />
Also if I run : adb shell dumpsys alarm
I can see :
ELAPSED_WAKEUP #0: Alarm{42d804e8 type 2 bp.headsup.mock}
operation=PendingIntent{42d0c230: PendingIntentRecord{42d0f000 bp.headsup.mock broadcastIntent}}
Mock in the above response is the sourceSet I'm using - dont know if it has anything to do with this I just mention it.
The problem is I never read in logcat the messages I have in onReceive on my Receiver class, and obviously no service starts. Anyone can help with that ? I'm using a device which runs with kitKat 4.4 (api 19) but I have tried it with an emulator too and the result was the same.
You're setting an ELAPSED_REALTIME alarm, which is based on the time since the last boot. However, you're passing it a starting time based on the "wall clock", so your alarm is actually set quite far in the future.
You can either change the alarm to an RTC type, or get the starting time from SystemClock.elapsedRealtime(). Given your described behavior, keeping the elapsed type and correcting the starting time seems appropriate.
Before closing this issue or marking as duplicate based on the title, it is different from the regular "cancel alarm using AlarmManager and PendingIntent" questions.
I am capable of creating and cancelling pending intents, as long as they are set for a time in the future and haven't gone off yet. I'm testing this using the following terminal command to view the PendingIntents before creating an alarm as well as after:
adb shell dumpsys alarm
Here is my code for scheduling alarms in my custom Alarm class:
/**
* Schedules a PendingIntent for the alarm.
* #param context Activity context
*/
public void scheduleAlarm(Context context) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyBroadcastReceiver.class);
Gson g = new Gson();
String s = g.toJson(this);
intent.putExtra("alarm", s);
String id = this.getId().replaceAll("[^0-9]+", "");
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, Integer.parseInt(id), intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, this.getHour());
calendar.set(Calendar.MINUTE, this.getMinute());
long calendarTime = calendar.getTimeInMillis();
am.setExact(AlarmManager.RTC_WAKEUP, calendarTime, alarmIntent);
}
Not surprisingly, before creating an alarm, there was no pending intent regarding my app's alarms in the terminal output. After creating the alarm, there was 1 pending intent related to my app in the terminal output, as seen below:
+Batch{b28a2db num=1 start=619295497 end=619385497}: + RTC #0: Alarm{24e4178 tag
alarm:com.google.android.location.internal.action.ULR_BAROMETER_READ_ALARM type 1 when 1501206840000 com.google.android.gms} +
tag=alarm:com.google.android.location.internal.action.ULR_BAROMETER_READ_ALARM + type=1 whenElapsed=+1m59s428ms when=2017-07-27 21:54:00 + window=+1m30s0ms repeatInterval=120000 count=0 flags=0x0 +
operation=PendingIntent{cb99ddd: PendingIntentRecord{f0fbd52
com.google.android.gms startService}}
Note I don't have access to my home computer right now so I can't post exactly what it will be for my app, so I just grabbed the PendingIntent for a different app but it is the same structure.
I cancelled the alarm before it went off using the code found below, reran the adb command from before and the pending intent was no longer in the terminal output so everything worked great.
Here is my code for cancelling alarms:
/**
* Cancels the PendingIntent for the alarm.
* #param context
*/
public void cancelAlarm(Context context) {
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyBroadcastReceiver.class);
intent.putExtra("alarm", this);
String id = this.getId().replaceAll("[^0-9]+", "");
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, Integer.parseInt(id), intent, 0);
alarmIntent.cancel();
am.cancel(alarmIntent);
}
Now, if the PendingIntent was reached (BroadcastReceiver runs its onReceive() code and opens a custom activity), I get the following entry in the terminal output when I rerun that adb command:
u0a149:com.my.app +172ms running, 0 wakeups:
+172ms 0 wakes 3 alarms, last -5d8h25m0s423ms:
alarm:com.google.firebase.INSTANCE_ID_EVENT
but I can no longer see the PendingIntent, as expected. Whether I run the cancelAlarm() code or not, this entry will always stay here.
The result of this is that whenever I open the app after the PendingIntent has "gone off" and my BroadcastReceiver class runs it's code, the app acts as if the alarm is continually going off so it does this repeatedly, but like I said there's no PendingIntent entry in the adb output. I want to know how to shut this alarm off or "dismiss" it if you will.
Here is my BroadcastReceiver class:
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String alarm = intent.getStringExtra("alarm");
Intent myIntent = new Intent();
Toast.makeText(context, "Alarm is 1: " + alarm, Toast.LENGTH_SHORT).show();
myIntent.setClassName("com.my.package.name", "com.my.package.name.AlarmReceivedActivity");
myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
myIntent.putExtra("alarm", alarm);
context.startActivity(myIntent);
}
}
and here's my AlarmReceivedActivity:
public class AlarmReceivedActivity extends AppCompatActivity {
private Alarm alarmReceived;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alarm_received);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
TextView tvTime = (TextView) findViewById(R.id.tv_time);
String lTime = "11:05";
tvTime.setText(lTime);
Intent intent = getIntent();
String s = intent.getStringExtra("alarm");
Toast.makeText(getApplicationContext(), "Alarm is: " + s, Toast.LENGTH_SHORT).show();
Gson g = new Gson();
alarmReceived = g.fromJson(s, Alarm.class);
Uri ringtoneUri = Uri.parse(alarmReceived.getRingtone());
Toast.makeText(getApplicationContext(), "Ringtone is: " + alarmReceived.getRingtone(), Toast.LENGTH_SHORT).show();
try {
MediaPlayer mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(this, ringtoneUri);
final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mMediaPlayer.setLooping(false);
mMediaPlayer.prepare();
mMediaPlayer.start();
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Failed to play ringtone", Toast.LENGTH_SHORT).show();
}
}
public void dismissButtonClick(View view) {
Context context = getApplicationContext();
alarmReceived.cancelAlarm(context);
alarmReceived.setIsSet(false);
writeAlarmToSharedPrefs(alarmReceived, context);
alarmReceived.cancelAlarm(context);
System.exit(0);
}
private void writeAlarmToSharedPrefs(Alarm alarmReceived, Context context) {
String alarm = getAlarmObjectAsJson(alarmReceived);
SharedPreferences sPrefs = getApplicationContext().getSharedPreferences("Sleepin", MODE_PRIVATE);
SharedPreferences.Editor pe = sPrefs.edit();
pe.putString(alarmReceived.getId(), alarm);
pe.apply();
}
private String getAlarmObjectAsJson(Alarm a) {
Gson g = new Gson();
return g.toJson(a);
}
public void snoozeButtonClick(View view) {
}
}
So I have a couple questions:
1) Since the PendingIntent has been reached, what is this entry in the adb command output referred to as? A "ReachedPendingIntent" (obviously not called this but I hope you get where I'm going with this).
2) How do I stop my app from running the BroadcastReceiver code? My workaround right now is to clear the app's data and cache. After I do this, the second entry from the adb command doesn't appear and the BroadcastReceiver stops running it's code.
I created an app to send message using alarm manager but if i put an alarm for a long duration the app is killed by android automatically, so i need to prevent the app from getting killed.Please tell me how can I do it.
Calendar cal = Calendar.getInstance();
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
if (currentApiVersion > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
cal.set(Calendar.MINUTE, time_picker.getMinute());
cal.set(Calendar.HOUR_OF_DAY, time_picker.getHour());
} else {
//Setting the date and time from the time picker
cal.set(Calendar.MINUTE, time_picker.getCurrentMinute());
cal.set(Calendar.HOUR_OF_DAY, time_picker.getCurrentHour());
}
//System clock time
Calendar c = Calendar.getInstance();
Long a ;//=(long) (Calendar.getInstance().get(Calendar.SECOND) * 1000);
if(cal.get(Calendar.HOUR_OF_DAY) < c.get(Calendar.HOUR_OF_DAY))
h = (cal.get(Calendar.HOUR_OF_DAY) + 24 - c.get(Calendar.HOUR_OF_DAY)) * 60;
else
h = (cal.get(Calendar.HOUR_OF_DAY) - c.get(Calendar.HOUR_OF_DAY * 60;
m = (cal.get(Calendar.MINUTE) - c.get(Calendar.MINUTE));
a = (m + h) * 60;
myIntent = new Intent(this, MyReceiver.class);
myIntent.putExtra("pos", array.select);
//Pending Intent for sending the intent afterwards
pendingIntent[array.select] = PendingIntent.getBroadcast(this.getApplicationContext(), array.select, myIntent, 0);
alarmManager[array.select] = (AlarmManager) (this.getSystemService(Context.ALARM_SERVICE));
alarmManager[array.select].set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + a * 1000, pendingIntent[array.select]);
pendingarray.add(pendingIntent[array.select]);
sms_list.Phone[array.select] = Phone;
Intent back = new Intent(this, sms_list.class);
back.putExtra("PHONE", Phone);
back.putExtra("Flag",2);
back.putExtra("MSG", Message);
back.putExtra("HOUR", (int) cal.get(Calendar.HOUR_OF_DAY));
back.putExtra("MIN", (int) cal.get(Calendar.MINUTE));
back.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(back);
If the answer is wake lock can you please tell me where to use it.
You can use a service to do it, this will also work after the device is rebooted. You also have to make the service foreground to prevent the system from killing it. It can be done by adding an ongoing notification. See the service code below.
In your Manifest add the following
<receiver
android:name=".Autostart"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<service
android:name=".StarterService"
android:enabled="true"
android:exported="true" />
Then create a new class as follows:
public class Autostart extends BroadcastReceiver {
/**
* Listens for Android's BOOT_COMPLETED broadcast and then executes
* the onReceive() method.
*/
#Override
public void onReceive(Context context, Intent arg1) {
Log.d("Autostart", "BOOT_COMPLETED broadcast received. Executing starter service.");
Intent intent = new Intent(context, StarterService.class);
context.startService(intent);
}
}
And finally your service as follows:
public class StarterService extends Service {
private static final String TAG = "MyService";
/**
* starts the AlarmManager.
*/
#Override
public void onCreate() {
super.onCreate();
//TODO: Start ongoing notification here to make service foreground
}
#Override
public void onStart(Intent intent, int startid) {
//TODO: Put your AlarmManager code here
//TODO: you also need to add some logic to check if some previous work is pending in case of a device reboot
Log.d(TAG, "onStart");
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
//TODO: cancel the notification
Log.d(TAG, "onDestroy");
}
}
Now all you need to do is call the service whenever you need to send the message.
PS: I know an answer is accepted but hope this helps you or someone else.
An alarm should be triggered in a Broadcast Receiver.
If it performs long-lived operations, you should then use threads or Services. Both of them can be launched from a receiver.
EDIT
As a short example, I use this method in a button's onClickListener in the activity :
scheduleAlarm(name);
Method :
public void scheduleAlarm(String client)
{
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String delay = sharedPref.getString(SettingsActivity.PREF_DELIVERY_DELAY, "48");
// time at which alarm will be scheduled here alarm is scheduled at 1 day from current time,
// we fetch the current time in milliseconds and added 1 day time
// i.e. 24*60*60*1000= 86,400,000 milliseconds in a day
Long time = new GregorianCalendar().getTimeInMillis()+ Integer.parseInt(delay) * 1000; //todo change seconds to hours
// create an Intent and set the class which will execute when Alarm triggers, here we have
// given AlarmReciever in the Intent, the onRecieve() method of this class will execute when
// alarm triggers and
//we will write the code to send SMS inside onRecieve() method pf Alarmreciever class
Intent intentAlarm = new Intent(this, AlarmReceiver.class);
intentAlarm.putExtra("CLIENT", client);
// create the object
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//set the alarm for particular time
//todo string res
alarmManager.set(AlarmManager.RTC_WAKEUP,time, PendingIntent.getBroadcast(this,1, intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT));
Toast.makeText(this, "Alarm Scheduled in " + delay + " hours", Toast.LENGTH_LONG).show();
}
And finally, the AlarmReceiver.java
package com.patrickmiller.test2;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent)
{
Toast.makeText(context, "Alarm received", Toast.LENGTH_SHORT).show();
String client = intent.getStringExtra("CLIENT");
Notify(context, client);
}
public void Notify(Context context, String client) {
//todo expanded layout with options Fiche de contact | Rapport and cover image
//todo send name, address, phone, email and id through Intent to ContactClientActivity
//todo delete notification when generated
try {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
//todo set notification icon, content title and content text as string resources
.setSmallIcon(R.drawable.warning)
.setContentTitle(client)
.setContentText("N'oubliez pas de générer le rapport du client");
Intent resultIntent = new Intent(context, ContactClientActivity.class);
//todo may need to expend instead of calling activity. Buttons will do.
// Because clicking the notification opens a new ("special") activity, there's
// no need to create an artificial back stack.
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
context,
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
// Sets an ID for the notification
int mNotificationId = 001;
// Gets an instance of the NotificationManager service
NotificationManager mNotifyMgr = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
// Builds the notification and issues it.
mNotifyMgr.notify(mNotificationId, mBuilder.build());
}
catch(Exception e) {
Toast.makeText(context, String.valueOf(e), Toast.LENGTH_LONG).show();
}
}
}
You don't have to care about the client's thing. Just the way I scheduled the alarm..
My operation is a short-lived one, which is sending a notification. If you plan a long-lived operation, you should start a service or a thread from the receiver (onReceive callback method).
ok, your app is finished because is running in the main thread, so you need to make this process in other thread that is not killed when the app is closed. check this documentation from the official page. if you decide start using asyncTask class check this reference
I am experiencing some lag/black screen in my application since I start using AlarmManager + BroadcastReceiver. I have 2 BroadcastReceivers, one to when the phone gets restarted and another that AlarmManager call in the given period of time to send data to the server.
This is the code for BootReceiver to start the alarmManager once the cellphone is rebooted (it is working so far):
private final String BOOT_COMPLETED_ACTION = "android.intent.action.BOOT_COMPLETED";
#Override
public void onReceive(Context context, Intent intent) {
// when the boot is completed, restart the alarm manager
if(intent.getAction().equals(BOOT_COMPLETED_ACTION)){
SharedPreferences mPrefs = context.getSharedPreferences("GPS_TRACKING", Context.MODE_PRIVATE);
if (mPrefs.getBoolean("hasGeolocation", false) &&
!mPrefs.getBoolean("isThreadOn", false)){
EngineerTracker tracker = new EngineerTracker(context);
try {
tracker.startEngineerTrackingLocation();
} catch (ApplicationException e) {
e.printStackTrace();
}
}
}
}
The method to start and stop the alarm manager is this:
public void startEngineerTrackingLocation() throws ApplicationException{
PendingIntent pendingIntent = null;
AlarmManager manager = null;
ProjectGeospatialConfig geospatialConfig;
// check if the intent is running, if it is not, start it
if (PendingIntent.getBroadcast(context, 0,
new Intent(context, EngineerGeospatialTrackingReceiver.class),
PendingIntent.FLAG_NO_CREATE) == null){
// fetch the geospatial configuration, it may come null, so verify before using
geospatialConfig = getFirstFoundGeospatialConfiguration();
// if not null and use gps
if (geospatialConfig != null && geospatialConfig.isUseGps()){
// session information
SessionInformationDTO sessionInformation = dao.getObjectForKey(SqlLiteStorageKey.USER_INFORMATION);
Integer currentResourceId = sessionInformation.getSecurityHandler().getCurrentUser().getId();
// Retrieve a PendingIntent that will perform a broadcast and add resource id as extra
Intent alarmIntent = new Intent(context, EngineerGeospatialTrackingReceiver.class);
alarmIntent.putExtra("resourceId", currentResourceId.toString());
// set pending intent
if (pendingIntent == null){
pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
}
// set manager
if (manager == null){
manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
// set interval between alarms
int interval = (geospatialConfig.getGpsTrackingInterval() *1000) * 60;
// set alarm repetition
manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
interval, pendingIntent);
// set variables for gps tracking
SharedPreferences mPrefs = getApplicationContext().getSharedPreferences("GPS_TRACKING", Context.MODE_PRIVATE);
Editor editor = mPrefs.edit();
// these variables will be measured once db is set
editor.putBoolean("hasExecuted", false);
editor.commit();
}
}
}
both are also working so far, the flag is meant to know when the service has been executed once and will not attempt again at the basic activity (template for all activitied)
The broadcast that is invoked in the alarm manager to send the information in the defined interval is this:
public class EngineerGeospatialTrackingReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String resourceId = intent.getStringExtra("id");
sendLocation(context, resourceId);
}
private void sendLocation(final Context context, final String resourceId){
new RemoteRequestTask<Void>(null, false, null) {
#Override
public Void executeTask() throws ApplicationException {
// working code
}
#Override
public void completed(Void refreshed) {
}
#Override
public void onException(final ApplicationException ex) {
}
}.start();
}}
Both receivers were added to the AndroidManifest. Beside the slowness, i also get a black screen when transitioning from an activity to another.
Use Traceview to determine where you are spending your time, and consider enabling StrictMode to point out where you are doing unfortunate things on the main application thread.
You want onReceive() to be very quick, ideally under 1ms, as. However, it looks like you might be doing database I/O in there (e.g., references to dao), which means that work should be handled off the main application thread, perhaps by an IntentService that you start from onReceive().