I have a service which downloads data, runs in a separate process (so that it doesn't die/restart when the app is closed) and shows a notification with it's progress. I want to be able to stop the service if the user swipe-deletes the notification, but have so far been unable to do it. Relevant code below:
DatabaseDownloadService.java
public class DatabaseDownloadService extends Service
{
private final static int NOTIFICATION_ID = 1337;
private final static String NOTIFICATION_DISMISSAL_TAG = "my_notification_dismissal_tag";
private NotificationManager mNotificationManager;
#Override
public void onCreate()
{
super.onCreate();
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = getNotification("Downloading database...");
startForeground(NOTIFICATION_ID, notification);
startDownloadingStuff();
}
private Notification getNotification(String text)
{
NotificationDismissedReceiver receiver = new NotificationDismissedReceiver();
registerReceiver(receiver, new IntentFilter(NOTIFICATION_DISMISSAL_TAG));
Intent intent = new Intent(this, NotificationDismissedReceiver.class);
PendingIntent deleteIntent = PendingIntent.getBroadcast(this, NOTIFICATION_ID, intent, 0);
return new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("My Awesome App")
.setContentText(text)
.setDeleteIntent(deleteIntent)
.build();
}
public class NotificationDismissedReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
int notificationId = intent.getExtras().getInt(NOTIFICATION_DISMISSAL_TAG);
Toast.makeText(context, "Download cancelled", Toast.LENGTH_SHORT).show();
// Do more logic stuff here once this works...
}
}
}
AndroidManifest.xml
<application
... properties and activities go here...>
<service
android:name=".DatabaseDownloadService"
android:process=":dds_process"
android:enabled="true"/>
<receiver
android:name="com.myapp.DatabaseDownloadService$NotificationDismissedReceiver"
android:exported="false"/>
</application>
As far as I can tell, the .setDeleteIntent() should make the notification swipe-deletable, which should then send a broadcast, which should then be caught by my NotificationDismissedReceiver. However, as it stands, I can't even swipe-delete the notification, and I never see the "Download cancelled" Toast...
You can call to stop the service from foreground, passing false, means not remove the notification. For Android N and above, you can pass STOP_FOREGROUND_DETACH also.
stopForeground(false);
After that you can stop the service by yourself also.
stopSelf();
Instead of using startForeground(), use :
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify("tag", NOTIFICATION_ID, notification);
Related
I am making a simple app that that consists of a service that pops a notification several times a day. I am trying to make this with a Alarm. All works perfect if I don't close the app. However, if I close the app when the receiver is triggered I get the message "the app has stopped working" or whatever.
I was wandering if it is because the service cannot work without an activity of the app or something? Any insights?
Here is my code:
The receiver:
public class AlarmReceiver extends BroadcastReceiver {
public static final int REQUEST_CODE = 12345;
public static final String ACTION = "com.ddimitrovd.hap4eapp4e.alarm";
// Triggered by the Alarm periodically (starts the service to run task)
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, MainIntentService.class);
i.putExtra("foo", "bar");
context.startService(i);
}
}
Here is the Service:
public class MainIntentService extends IntentService {
int noteID = 1;
String chanelID;
public MainIntentService() {
super("MainIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
chanelID = getString(R.string.channel_ID);
Toast.makeText(this, "My Service Started", Toast.LENGTH_LONG).show();
// Do the task here
createNotificationChannel();
popNotif();
}
public void onDestroy() {
Toast.makeText(this, "My Service Stopped", Toast.LENGTH_LONG).show();
}
private void popNotif() {
// Create an explicit intent for an Activity in your app
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, chanelID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("My notification")
.setContentText("Hello World!")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
// Set the intent that will fire when the user taps the notification
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(noteID, mBuilder.build());
}
private void createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(chanelID, name, importance);
channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
}
I am certain the alarm triggers the receiver.
Thank you!
I think that I found the problem. I reckon that my IntentService can not execute complexly because the BroadcastReceiver process is killed before it can do it. More info here.
What I did was simply to pop my notification in the receiver, but I guess a better solution would be to start another thread or to put it async.
I am working on an App where I want to show Push Notifications. Please note that since it is my client's requirement NOT to use any third party services, so using GCM/Firebase is out of question.
I am successfully able to show the notification from Service using the below code.
public class SendNotificationService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
CharSequence title = "Notification Title";
CharSequence message = "This is a test notification.";
Drawable drawable= ContextCompat.getDrawable(this,R.drawable.brand_icon_color);
Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.brand_icon_small_color)
.setLargeIcon(bitmap)
.setAutoCancel(true)
.setContentTitle(title)
.setOngoing(false);
mBuilder.setContentText(message);
mBuilder.setTicker(message);
mBuilder.setWhen(System.currentTimeMillis());
NotificationManager notificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(), 0);
mBuilder.setContentIntent(pendingIntent);
notificationManager.notify(0, mBuilder.build());
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "Notifications Stopped...", Toast.LENGTH_LONG).show();
}
}
I am starting this service through my AsyncTask onPostExecute method.
Intent intentService = new Intent(context, SendNotificationService.class);
context.startService(intentService);
I created this following a number of tutorials and found that if I will go to my Running Apps in Android Settings, I will be able to see this service running. But I was unable to locate any such service.
Now the problem is when I close my Application, the notification also disappears. I want it to stay until the user takes any action.
In addition to this, I want this service to start with phone startup even if the app is not started.
1) Add the permission to the manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
2) Add a receiver to the manifest to run on boot:
<receiver android:name="com.example.MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
In MyBroadcastReceiver.java:
package com.example;
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent startServiceIntent = new Intent(context, MyService.class);
context.startService(startServiceIntent);
}
}
I'm trying to create notifications for my app that sends notifications when a certain task is due. Each task has a deadline time so I want to send a notification for each task when their deadline has passed.
My main class is called RecyclerViewDemoActivity and inside the onCreate() I have this:
public void setNotification()
{
Intent intent=new Intent(this,NotificationClass.class);
AlarmManager manager=(AlarmManager)getSystemService(Activity.ALARM_SERVICE);
PendingIntent pendingIntent= PendingIntent.getService(this, 0, intent, 0);
// hardcoding the time just for this example
manager.set(AlarmManager.RTC_WAKEUP,1449208683000,pendingIntent);
}
and I have a NotificationClass that looks like this:
public class NotificationClass extends Service {
#Override
public void onCreate()
{
Intent resultIntent=new Intent(this, RecyclerViewDemoActivity.class);
PendingIntent pIntent=PendingIntent.getActivity(this,0,resultIntent,0);
Notification nBuilder= new Notification.Builder(this)
.setContentTitle("This task is due!")
.setContentIntent(pIntent)
.setSmallIcon(R.mipmap.ic_launcher)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nBuilder.flags |=Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(1,nBuilder);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.grokkingandroid.samplesapp.samples.recyclerviewdemo" >
<uses-permission android:name="android.permission.READ_CALENDAR"></uses-permission>
<uses-permission android:name="android.permission.WRITE_CALENDAR"></uses-permission>
<application
android:name="com.teamvallartas.autodue.RecyclerViewDemoApp"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.teamv.RecyclerViewDemoActivity"
android:label="#string/app_name"
android:configChanges="orientation"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
I looked at this resource for the code above and also Vogella's resource on notifications but I don't know why this is not working.
Check if onCreate of Service class is getting called. If it is, then the problem is "You placed your code at wrong method.".
You need to move code into onStartCommand(Intent intent, int flags, int startId).
Like
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent resultIntent=new Intent(this, RecyclerViewDemoActivity.class);
PendingIntent pIntent=PendingIntent.getActivity(this,0,resultIntent,0);
Notification nBuilder= new Notification.Builder(this)
.setContentTitle("This task is due!")
.setContentIntent(pIntent)
.setSmallIcon(R.mipmap.ic_launcher)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nBuilder.flags |=Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(1,nBuilder);
// If we get killed, after returning from here, restart
return START_STICKY;
}
Update your manifest and add this entry to it
<service android:name=".NotificationClass" />
you can do it in this way ,
1st > create one local broadcast receiver ,
inside onReceive() method , put your code stuff for generate notification
,that is your setNotification() method .
2nd > you just register that broadcast receiver inside onCreate() and unregister it inside onPause() or onDestory() method . like this ...
ReceiverActivity.java
public void onCreate(Bundle savedInstanceState) {
...
// Register your broadcast receiver here ...
// with actions named "custom-event-name"...
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("custom-event-name"));
}
protected void onDestroy() {
// Unregister your receiver
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
super.onDestroy();
}
//here comes our receiver ...
// Our handler for received Intents. This will be called whenever an Intent
// with an action named "custom-event-name" is broadcasted.
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//called setNotification() here ...
}
};
Now let suppose you want to generate notification on click event of your button then fire intent like this,
Intent intent = new Intent("custom-event-name");
// You can also include some extra data.
intent.putExtra("message", "Its me!!!!");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
your **setNotification()**should be like this ..
public void Notification(Context context, String message) {
// Set Notification Title
String strtitle = context.getString(R.string.notificationtitle);
// Open NotificationView Class on Notification Click
Intent intent = new Intent(context, NotificationView.class);
// Send data to NotificationView Class
intent.putExtra("title", strtitle);
intent.putExtra("text", message);
// Open NotificationView.java Activity
PendingIntent pIntent = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// Create Notification using NotificationCompat.Builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(
context)
// Set Icon
.setSmallIcon(R.drawable.logosmall)
// Set Ticker Message
.setTicker(message)
// Set Title
.setContentTitle(context.getString(R.string.notificationtitle))
// Set Text
.setContentText(message)
// Add an Action Button below Notification
.addAction(R.drawable.ic_launcher, "Action Button", pIntent)
// Set PendingIntent into Notification
.setContentIntent(pIntent)
// Dismiss Notification
.setAutoCancel(true);
// Create Notification Manager
NotificationManager notificationmanager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
// Build Notification with Notification Manager
notificationmanager.notify(0, builder.build());
}
In my application I want show a notification in some cases.
When notification is active I do not want to create notification again.
I have activity recognition in my app and when it's detected that I am in car it starts to sound notification every second.
How could I prevent a new build notification if there is at least one active notification there?
Here is my code what I tried:
Intent closeIntent;
Intent showIntent;
if (isStart){
closeIntent = new Intent(this, SwitchButtonListener1.class);
} else {
closeIntent = new Intent(this, SwitchButtonListener2.class);
}
closeIntent.setAction("No");
PendingIntent pendingIntentClose = PendingIntent.getBroadcast(this, 0,
closeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action closeAction = new NotificationCompat.Action(R.drawable.btn_close_gray, "No", pendingIntentClose);
if (isStart){
showIntent = new Intent(this, SwitchButtonListener1.class);
} else {
showIntent = new Intent(this, SwitchButtonListener2.class);
}
showIntent.setAction("Yes");
PendingIntent pendingIntentShow = PendingIntent.getBroadcast(this, 0,
showIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action showAction = new NotificationCompat.Action(R.drawable.ic_tick, "Yes", pendingIntentShow);
Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_stat_milebox)
.setContentTitle(title)
.setContentText(message)
.addAction(showAction)
.addAction(closeAction);
builder.setSound(alarmSound);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(100, builder.build());
Though it is an old question, but I think this answer might help others in the future:
In a case like this, when the user needs to be notified only once and the event is ongoing then using .setOnlyAlertOnce(true) and setOngoing(true) with the builder will solve the problem.
Documentation:
setOnlyAlertOnce(true): Set this flag if you would only like the sound, vibrate and ticker to be played if the notification is not already showing.
setOngoing(true): Set whether this is an ongoing notification. Ongoing notifications cannot be dismissed by the user, so your application or service must take care of canceling them. They are typically used to indicate a background task that the user is actively engaged with (e.g., playing music) or is pending in some way and therefore occupying the device (e.g., a file download, sync operation, active network connection).
Notification notification = new NotificationCompat.Builder(this, notificationChannel.getId())
.....
.....
.setOngoing(true)
.setOnlyAlertOnce(true)
.....
.....
.build();
Objects.requireNonNull(notificationManager).notify(notificationId, notification);
You can try the following as a sketch:
public class MediaNotificationManager extends BroadcastReceiver {
private final NotificationManager mNotificationManager;
private Context ctx;
private boolean mStarted = false;
public MediaNotificationManager(Context ctx) {
mCtx = ctx;
mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
// Cancel all notifications to handle the case where the Service was killed and
// restarted by the system.
mNotificationManager.cancelAll();
}
/**
* Posts the notification and starts tracking the session to keep it
* updated. The notification will automatically be removed if the session is
* destroyed before {#link #stopNotification} is called.
*/
public void startNotification() {
if (!mStarted) {
// The notification must be updated after setting started to true
Notification notification = createNotification();
if (notification != null) {
mStarted = true;
}
}
}
/**
* Removes the notification and stops tracking the session. If the session
* was destroyed this has no effect.
*/
public void stopNotification() {
if (mStarted) {
mStarted = false;
try {
mNotificationManager.cancel(NOTIFICATION_ID);
} catch (IllegalArgumentException ex) {
// ignore if the receiver is not registered.
}
}
}
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
LogHelper.d(TAG, "Received intent with action " + action);
switch (action) {
//do something with this.
}
}
private Notification createNotification() {
//create and return the notification
}
}
For a bit more read this:
I also used this notification in my code:
https://github.com/googlesamples/android-UniversalMusicPlayer/blob/master/mobile/src/main/java/com/example/android/uamp/MediaNotificationManager.java
I'm using an android notification to alert the user once a service is finished (success or failure), and I want to delete local files once the process is done.
My problem is that in the event of failure - I want to let the user a "retry" option. and if he chooses not to retry and to dismiss the notification I want to delete local files saved for the process purposes (images...).
Is there a way to catch the notification's swipe-to-dismiss event?
DeleteIntent:
DeleteIntent is a PendingIntent object that can be associated with a notification and gets fired when the notification gets deleted, ether by :
User specific action
User Delete all the notifications.
You can set the Pending Intent to a broadcast Receiver and then perform any action you want.
Intent intent = new Intent(this, MyBroadcastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 0, intent, 0);
Builder builder = new Notification.Builder(this):
..... code for your notification
builder.setDeleteIntent(pendingIntent);
MyBroadcastReceiver
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
.... code to handle cancel
}
}
A fully flushed out answer (with thanks to Mr. Me for the answer):
1) Create a receiver to handle the swipe-to-dismiss event:
public class NotificationDismissedReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
int notificationId = intent.getExtras().getInt("com.my.app.notificationId");
/* Your code to handle the event here */
}
}
2) Add an entry to your manifest:
<receiver
android:name="com.my.app.receiver.NotificationDismissedReceiver"
android:exported="false" >
</receiver>
3) Create the pending intent using a unique id for the pending intent (the notification id is used here) as without this the same extras will be reused for each dismissal event:
private PendingIntent createOnDismissedIntent(Context context, int notificationId) {
Intent intent = new Intent(context, NotificationDismissedReceiver.class);
intent.putExtra("com.my.app.notificationId", notificationId);
PendingIntent pendingIntent =
PendingIntent.getBroadcast(context.getApplicationContext(),
notificationId, intent, 0);
return pendingIntent;
}
4) Build your notification:
Notification notification = new NotificationCompat.Builder(context)
.setContentTitle("My App")
.setContentText("hello world")
.setWhen(notificationTime)
.setDeleteIntent(createOnDismissedIntent(context, notificationId))
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notificationId, notification);
Another Idea:
if you create a notification normally you also need the actions one, two or 3 of them. I've created a "NotifyManager" it creates all notifications i need and also receive all Intent calls.
So i can manage all the actions AND also the catch the dismiss event at ONE place.
public class NotifyPerformService extends IntentService {
#Inject NotificationManager notificationManager;
public NotifyPerformService() {
super("NotifyService");
...//some Dagger stuff
}
#Override
public void onHandleIntent(Intent intent) {
notificationManager.performNotifyCall(intent);
}
to create the deleteIntent use this (in the NotificationManager):
private PendingIntent createOnDismissedIntent(Context context) {
Intent intent = new Intent(context, NotifyPerformMailService.class).setAction("ACTION_NOTIFY_DELETED");
PendingIntent pendingIntent = PendingIntent.getService(context, SOME_NOTIFY_DELETED_ID, intent, 0);
return pendingIntent;
}
and THAT i use to set the delete Intent like this (in the NotificationManager):
private NotificationCompat.Builder setNotificationStandardValues(Context context, long when){
String subText = "some string";
NotificationCompat.Builder builder = new NotificationCompat.Builder(context.getApplicationContext());
builder
.setLights(ContextUtils.getResourceColor(R.color.primary) , 1800, 3500) //Set the argb value that you would like the LED on the device to blink, as well as the rate
.setAutoCancel(true) //Setting this flag will make it so the notification is automatically canceled when the user clicks it in the panel.
.setWhen(when) //Set the time that the event occurred. Notifications in the panel are sorted by this time.
.setVibrate(new long[]{1000, 1000}) //Set the vibration pattern to use.
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))
.setSmallIcon(R.drawable.ic_white_24dp)
.setGroup(NOTIFY_GROUP)
.setContentInfo(subText)
.setDeleteIntent(createOnDismissedIntent(context))
;
return builder;
}
and finally in the same NotificationManager is the perform function:
public void performNotifyCall(Intent intent) {
String action = intent.getAction();
boolean success = false;
if(action.equals(ACTION_DELETE)) {
success = delete(...);
}
if(action.equals(ACTION_SHOW)) {
success = showDetails(...);
}
if(action.equals("ACTION_NOTIFY_DELETED")) {
success = true;
}
if(success == false){
return;
}
//some cleaning stuff
}