I have an app that schedules a bunch of notifications (user has to answer questionnaires) locally using AlarmManager. The notification should show at certain points in the future.
I schedule the notifications like this:
private void scheduleNotification(Notification notification, int delay, int scheduleId, int notificationId) {
Intent notificationIntent = new Intent(context, NotificationPublisher.class);
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID, notificationId);
notificationIntent.putExtra(NotificationPublisher.INTENT, notification);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, scheduleId, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, delay);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
The intent is received by a BroadcastReceiver that calls notify on the notification attached to the intent.
public class NotificationPublisher extends BroadcastReceiver {
public static String NOTIFICATION_ID = "notification-id";
public static String INTENT = "notification";
#Override
public void onReceive(Context context, Intent intent) {
if (intent.hasExtra(INTENT)) {
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
Notification notification = intent.getParcelableExtra(INTENT);
int id = intent.getIntExtra(NOTIFICATION_ID, 0);
notificationManager.notify(id, notification);
}
}
}
This works fine so far. The problem that I'm facing is that I only want to show the notification if the app is currently not open/shown. If it's open I want to show an AlertDialog instead.
I know that it might be a better idea to put only the plain content of the notification into the intent and only build it when it should be displayed and I want to refactor that later on.
My main problem is, how do I determine in the onReceive of my broadcast receiver if the app is currently showing to decide if a notification or an alert should be displayed?
Or is there an entirely different approach that might work better (for example using WorkManager)?
I think you can handle it on your BroadcastReceiver
public void onReceive(Context context, Intent intent) {
if (isForeground(context))
// AlertDialog
else
// Notification
}
public boolean isForeground(Context mContext) {
ActivityManager activityManager = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.AppTask> tasks = activityManager.getAppTasks();
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).getTaskInfo().topActivity;
return topActivity.getPackageName().equals(mContext.getPackageName());
}
return true;
}
Related
I am trying to develop an application in which the user can create more than 1 alarms for the same time ,say, 09:48. And for this time, each alarm has to create its own notification which leads the user to an activity when the notification is clicked. The other alarms which were scheduled at the same time must keep ringing until their notifications are clicked. Now, I am able to create alarms for different times and get their corresponding notifications which lead to an activity in which the user can dismiss or snooze the alarm. However, when I schedule more than one alarm for the same time, only 1 alarm is ringing and the other one gets lost. I share my code pieces at the below.
public void schedule(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmBroadcastReceiver.class);
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(context, alarmId, intent, 0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
alarmManager.setExact(
AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(),
alarmPendingIntent
);
In my broadcastReceiver class:
private void startAlarmService(Context context, Intent intent) {
Intent intentService = new Intent(context, AlarmService.class);
Log.println(Log.DEBUG, "DEBUG", "************Alarm Broadcast Receiver**********");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intentService);
} else {
context.startService(intentService);
}
}
In my AlarmService :
#Override
public void onCreate() {
super.onCreate();
mediaPlayer = MediaPlayer.create(this, R.raw.alarm);
mediaPlayer.setLooping(true);
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, RingActivity.class);
int requestCode = new Random().nextInt(Integer.MAX_VALUE);
PendingIntent pendingIntent = PendingIntent.getActivity(this, requestCode, notificationIntent, 0);
//String alarmTitle = String.format("%s Alarm", intent.getStringExtra(TITLE));
int notificationId = new Random().nextInt(Integer.MAX_VALUE);
Notification notification = new NotificationCompat.Builder(this, String.valueOf(notificationId))
.setContentTitle("PASS1")
.setContentText("PASS2")
.setSmallIcon(R.drawable.pill)
.setContentIntent(pendingIntent)
.build();
mediaPlayer.start();
long[] pattern = { 0, 100, 1000 };
vibrator.vibrate(pattern, 0);
startForeground(1, notification);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mediaPlayer.stop();
vibrator.cancel();
}
in this code
startForeground(1, notification);
you are using 1 as the id.
If a notification with the same id has already been posted by your application and has not yet been canceled, it will be replaced by the updated information.
So the second alarm notification with the same id (because you are using a fixed value of 1) is replacing the old one
I am trying to create an app that will open another app at a specified time. To do this, I used an AlarmManager that starts a service. It works just fine if my app is open when the alarm is triggered. I get a notification that the service started, and the other app opens. However, if my app is in the background (after pressing the home button), and the alarm triggers, I get a notification that the service started, but the other app does not launch. What am I doing wrong? I am testing this on a Pixel 3 emulator running API level 29 (Android 10/Q).
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final int REQUEST_CODE=101;
public static int aHour;
public static int aMinute;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void setAlarm() {
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent(this, amReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, aHour);
calendar.set(Calendar.MINUTE, aMinute);
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
//Some code that sets aHour and aMinute
//Some code that triggers setAlarm()
}
amReciever.java
public class amReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, launcherService.class);
ContextCompat.startForegroundService(getApplicationContext(), i);
}
}
launcherService.java
public class launcherService extends Service {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText("App is launching.")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
Intent launcher = getApplicationContext().getPackageManager().getLaunchIntentForPackage("com.example.app");
if (launcher != null) {
startActivity(launcher);
}
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<service android:name=".launcherService"
android:enabled="true"
android:exported="true" />
As of Android 10 (API level 29), you cannot start activities from the background anymore.
There are a number of exceptions to this rule that may or may not apply to your given scenario.
If none of the exceptions apply, you might want to consider displaying a high-priority notification, possibly with a full-screen Intent.
I am trying to send a http request to my server in the background after I closed the app. But the thread is always being killed. I already tried Workmanager, AlarmManager and BackgroundService. I have been searching in the internet for solutions for the last weeks and I couldn't find any solutions working in newer API's and without a ForegroundService which has to display a notification while running.
Starting AlarmManager:
//NotificationAlarm is the class implementing BroadcastReceiver
Intent intent = new Intent(context, NotificationAlarm.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, ALARM_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis(), 10000, pendingIntent);
Until about Android 6 the AlarmManager works to me. However in latest versions it keeps getting closed.
I used the following tutorial as template for BackgroundServices but it still did not work: https://medium.com/#raziaranisandhu/create-services-never-stop-in-android-b5dcfc5fb4b2
I'm Looking forward to an answer.
Did you ever tried ForegroundService?...
you can create class extended as service btw, just add the intent filter on the manifest.
Example from friend's class.
public class ForegroundService extends Service {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
//do heavy work on a background thread
//stopSelf();
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
And start it as
startForegroundService(context,ForegroundService.class);
There is a lot of options
(Foreground service will start a notification until its dead)
I have this app where i want to set a notification on a specific date the user defines. I saw lots of code and relative answers here on SO but for some reason nothing works so far.
In this test project i have 2 methods. One that sets an alarm to ring 5 seconds on future and one that assigns the desired date.
Problem:
If i set the alarm to a second delay it works fine. The notification is shown within 5 seconds after the alarm assignment. But if i use the method that passes a Calendar date, it does nothing. Not triggers no notification
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void setAlarmOnClick(View view){
// this method call works fine
setAlarm(getNotification("Date test"), 3000);
// this one doesn't
// setAlarm(getNotification("Date test"), getDate());
}
// Here nothing happens when the date comes
private void setAlarm(Notification notification, Calendar cal) {
Intent notificationIntent = new Intent(this, NotificationPublisher.class);
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID, 1);
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION, notification);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | Intent.FILL_IN_DATA);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),pendingIntent );
Toast.makeText(this, "Alarm worked.", Toast.LENGTH_LONG).show();
}
// This alarm get successfully trigger here showing the notification
private void setAlarm(Notification notification, int delay) {
Intent notificationIntent = new Intent(this, NotificationPublisher.class);
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID, 1);
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION, notification);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | Intent.FILL_IN_DATA);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, delay, pendingIntent );
Toast.makeText(this, "Alarm worked.", Toast.LENGTH_LONG).show();
}
private Calendar getDate(){
Calendar cal=Calendar.getInstance();
cal.set(Calendar.MONTH,8);
cal.set(Calendar.YEAR,2017);
cal.set(Calendar.DAY_OF_MONTH,29);
cal.set(Calendar.HOUR_OF_DAY,11);
cal.set(Calendar.MINUTE,47);
return cal;
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private Notification getNotification(String content) {
Notification.Builder builder = new Notification.Builder(this);
builder.setContentTitle("Scheduled Notification");
builder.setContentText(content);
builder.setSmallIcon(R.mipmap.ic_launcher);
return builder.build();
}
}
NotificationPublisher.java
public class NotificationPublisher extends BroadcastReceiver {
public static String NOTIFICATION_ID = "notification-id";
public static String NOTIFICATION = "notification";
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Alarm worked.", Toast.LENGTH_LONG).show();
int id = intent.getIntExtra(NOTIFICATION_ID, 0);
NotificationManager notofManager = (NotificationManager)context. getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = intent.getParcelableExtra(NOTIFICATION);
notofManager.notify(id, notification);
}
}
I would really appreciate anykind of help!
P.S.
How can i be sure that an alarm was successfully set on a date. Even if a code works and a date is set a week form now. How can i test that it will trigger then? Obviously we can't wait a week to see that. Changing the device date is working? Thank you
Calendar month goes from 0-11 instead of 1-12 as u assumed.
So something like:
cal.set(Calendar.Month, 7 );
would set your calendar month to August.
I am trying to get an android notification to show up at noon every day. The notification seems to show up once whenever the device is started, then somewhat sporadically afterwards.
Here is my service:
public class myService extends Service {
public static final String TAG = "LocationLoggerServiceManager";
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.v(TAG, "on onCreate");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, LoginActivity.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("App name")
.setContentText("Notification")
.setContentIntent(contentIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify("main", 1, mBuilder.build());
return super.onStartCommand(intent, flags, startId);
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
and Receiver:
public class MyBroadcastReceiver extends BroadcastReceiver {
public static final String TAG = "LocationLoggerServiceManager";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Broadcast Received");
handleMessage(context, intent);
}
private void handleMessage(Context context, Intent intent)
{
PendingIntent contentIntent = PendingIntent.getService(context, 0, new Intent(context, myService.class), 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(contentIntent);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 12);
calendar.set(Calendar.MINUTE, 00);
calendar.set(Calendar.SECOND, 00);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 24*60*60*1000 , contentIntent);
}
}
Any pointers are appreciated. Thank you.
I attempted to set up my own notification / alarm class. The only way I found to manage it was to extend the Android calendar.
If you would like to try it this way see this link for a start:
http://developer.android.com/guide/topics/providers/calendar-provider.html
I have an example of this approach however i am at work, I can provide my code later if you need it!
What may be happening is that the system kills your Service to free up memory and since the superclass's onStartCommand() returns START_STICKY, recreates it later, causing your notification to sporadically appear.
Really, if the Service's purpose is just to make a Notification consider moving that portion of code some sort of BroadcastReceiver or stop the Service after the Notification is created.