Android - Calling methods from notification action button - android

I know that you can launch Activities from the action buttons using PendingIntents. How do you make it so that the a method gets called when the user clicks the notification action button?
public static void createNotif(Context context){
...
drivingNotifBldr = (NotificationCompat.Builder) new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.steeringwheel)
.setContentTitle("NoTextZone")
.setContentText("Driving mode it ON!")
//Using this action button I would like to call logTest
.addAction(R.drawable.smallmanwalking, "Turn OFF driving mode", null)
.setOngoing(true);
...
}
public static void logTest(){
Log.d("Action Button", "Action Button Worked!");
}

You can't directly call methods when you click action buttons.
You have to use PendingIntent with BroadcastReceiver or Service to perform this. Here is an example of PendingIntent with BroadcastReciever.
First lets build a Notification
public static void createNotif(Context context){
...
//This is the intent of PendingIntent
Intent intentAction = new Intent(context,ActionReceiver.class);
//This is optional if you have more than one buttons and want to differentiate between two
intentAction.putExtra("action","actionName");
pIntentlogin = PendingIntent.getBroadcast(context,1,intentAction,PendingIntent.FLAG_UPDATE_CURRENT);
drivingNotifBldr = (NotificationCompat.Builder) new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.steeringwheel)
.setContentTitle("NoTextZone")
.setContentText("Driving mode it ON!")
//Using this action button I would like to call logTest
.addAction(R.drawable.smallmanwalking, "Turn OFF driving mode", pIntentlogin)
.setOngoing(true);
...
}
Now the receiver which will receive this Intent
public class ActionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Toast.makeText(context,"recieved",Toast.LENGTH_SHORT).show();
String action=intent.getStringExtra("action");
if(action.equals("action1")){
performAction1();
}
else if(action.equals("action2")){
performAction2();
}
//This is used to close the notification tray
Intent it = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.sendBroadcast(it);
}
public void performAction1(){
}
public void performAction2(){
}
}
Declare Broadcast Receiver in Manifest
<receiver android:name=".ActionReceiver" />
Hope it helps.

Related

Alarm Notification Data disappear after rebooting phone

I am building a notification app, that uses alarm manager to set multiple notifications to reminde user of daily tasks, when phone is On everything is good, but when a reboot the phone, the notification is showing up but the notification data are lost (for example notification content) those data are saved into intent from my first activity
this is code bellow. (i have added permission and receive action to my manifest)please I need some help
public class add_rreminde extends AppCompatActivity implements View.OnClickListener{
private int notificationId=1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_rreminde);
//buton ok
findViewById(R.id.but).setOnClickListener(this);
findViewById(R.id.cancel).setOnClickListener(this);
}
#Override
public void onClick(View view){
EditText remide=findViewById(R.id.edit);
DatePicker date=findViewById(R.id.date);
Intent intent=new Intent(add_rreminde.this,AlarmReciver.class);
intent.putExtra("notificationId",notificationId);
intent.putExtra("todo",remide.getText().toString());
final int _id=(int)System.currentTimeMillis();
PendingIntent alarmIntent=PendingIntent.getBroadcast(add_rreminde.this,_id,intent,PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarm=(AlarmManager)getSystemService(ALARM_SERVICE);
switch (view.getId()){
case(R.id.but):
int day=date.getDayOfMonth();
int month=date.getMonth();
int year=date.getYear();
//create date
Calendar startDate=Calendar.getInstance();
startDate.set(Calendar.DAY_OF_MONTH,day);
startDate.set(Calendar.MONTH,month);
startDate.set(Calendar.YEAR,year);
Long alarmStartDate=startDate.getTimeInMillis();
//setAlarme
alarm.set(AlarmManager.RTC_WAKEUP,alarmStartDate,alarmIntent);
Toast.makeText(getApplicationContext(),"done",Toast.LENGTH_LONG).show();
break;
case (R.id.cancel):
alarm.cancel(alarmIntent);
Toast.makeText(getApplicationContext(),"canceled",Toast.LENGTH_LONG).show();
break;
}
}
}
my reciver that push notification
public class AlarmReciver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//get id from notification id intent
int notification =intent.getIntExtra("notificationId",0);
String getMessage=intent.getStringExtra("todo");
//when notification is opened open mainActivity
Intent mainIntent=new Intent(context,add_rreminde.class);
PendingIntent contentIntent=PendingIntent.getActivity(context,0,mainIntent,0);
NotificationManager mynotificationManager=(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
//prepare notification
Notification.Builder builder=new Notification.Builder(context);
builder.setSmallIcon(R.drawable.assistant)
.setContentTitle("changer oil")
.setContentText(getMessage)
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setContentIntent(contentIntent)
.setPriority(Notification.PRIORITY_MAX)
.setDefaults(Notification.DEFAULT_ALL);
//notify
mynotificationManager.notify(notification,builder.build());
}
}
By default, all alarms are canceled when a device shuts down. To
prevent this from happening, you can design your application to
automatically restart a repeating alarm if the user reboots the
device.
so you could add a BroadcastReceiver to receive ACTION_BOOT_COMPLETED
firstly:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
and:
<receiver android:name=".SampleBootReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
now you set alarm again when device reboot:
public class SampleBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
// Set the alarm here.
}
}
}

Stop Service from PendingIntent after Notifcation is opened

I want the service to perform a stopForeground and a stopSelf after the notification is clicked followed by the running of pendingIntent.
I have tried using a BroadcastReceiver which is never called as I checked during debugging. I have added it to manifest as well.
Intent intentHide = new Intent(this, StopServiceReceiver.class);
PendingIntent hide = PendingIntent.getBroadcast(this, (int) System.currentTimeMillis(), intentHide, PendingIntent.FLAG_CANCEL_CURRENT);
Added it to the builder
builder.setContentIntent(hide);
And the Broadcast Rec is done separately -
public class StopServiceReceiver extends BroadcastReceiver {
public static final int REQUEST_CODE = 333;
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, TimerService.class);
context.stopService(service);
}
}
Manifest -
<receiver
android:name=".StopServiceReceiver"
android:enabled="true"
android:process=":remote" />
This is not working. The notification and the service both are alive.
Questions - Should I use addContent instead of setContentIntent ? If yes, then what should the parameters be ?
Is there anything I went wrong with? What could possibly be wrong with such kind of implementation? Thank you.
I had the same problem in the notification.
This code is working perfectly.
void creatnotifiaction()
{
public static final String STOP = "com.example.android";
public static final int REQUEST_CODE = 333;
filter = new IntentFilter();
filter.addAction(STOP);
Intent intentHide = new Intent(STOP);
PendingIntent hide = PendingIntent.getBroadcast(this,REQUEST_CODE,intentHide,PendingIntent.FLAG_CANCEL_CURRENT);
registerReceiver(broadcastReceiver, filter);
}
There no need to separate broadcast receiver use in same class.
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#SuppressLint("ResourceAsColor")
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Log.d("notification", "Received intent with action " + action);
switch (action) {
case STOP:
//your code to stop notifications or service.
break;
}
});
Let me know if that work for you.
Thanks...Happy coding.

Android, Display alertDialog instead of notification when app is open

I followed this developer tutorial, and have Geofencing working within my app, as expected.
A notification is sent when a Geofence Transition occurs, from within an IntentService:
#Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
...
sendNotification(geofenceTransitionDetails);
}
private void sendNotification(String notificationDetails) {
// Create an explicit content Intent that starts the main Activity.
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
// Construct a task stack.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Add the main Activity to the task stack as the parent.
stackBuilder.addParentStack(MainActivity.class);
// Push the content Intent onto the stack.
stackBuilder.addNextIntent(notificationIntent);
// Get a PendingIntent containing the entire back stack.
PendingIntent notificationPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
// Get a notification builder that's compatible with platform versions >= 4
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// Define the notification settings.
builder.setSmallIcon(R.mipmap.ic_launcher)
// In a real app, you may want to use a library like Volley
// to decode the Bitmap.
.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.mipmap.ic_launcher))
.setColor(Color.RED)
.setContentTitle(notificationDetails)
.setContentText("Return to app")
.setContentIntent(notificationPendingIntent);
// Dismiss notification once the user touches it.
builder.setAutoCancel(true);
// Get an instance of the Notification manager
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Issue the notification
mNotificationManager.notify(0, builder.build());
}
This is cookie-cutter from the tutorial. The intent is set-up in the Main activity:
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
How can I add functionality that suppresses the notifications if the app is open, and instead displays an AlertDialog to the user? Ideally, I'd like to be able to execute different tasks, depending on which view the user is currently in when the Geofence Transition occurs. Can I monitor/intercept the transition from within each view, or somehow globally?
Thanks in advance.
Some of the answers were incomplete, and so here is the complete solution to what I was looking for.
First off, set up MyApplication class, that implements ActivityLifecycleCallbacks:
public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks {
private static boolean isActive;
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
public static boolean isActivityVisible(){
return isActive;
}
#Override
public void onActivityResumed(Activity activity) {
isActive = true;
}
#Override
public void onActivityPaused(Activity activity) {
isActive = false;
}
... no other methods need to be used, but there are more that
... must be included for the ActivityLifecycleCallbacks
}
Be sure to name this in your manifest (only name line was added, rest is default):
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme"
android:hardwareAccelerated="true">
What was done above is used to track the lifecycle of your app. You can use this to check if your app is currently in the foreground or not.
Next is to set up a BroadcastReceiver, wherever you would like code to run (in the event that the app is open when the trigger occurs). In this case, it is in my MainActivity:
protected BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
... Do whatever you want here
Toast.makeText(...).show();
}
};
Register the receiver in your onCreate of the same activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
LocalBroadcastManager.getInstance(this).registerReceiver(mNotificationReceiver, new IntentFilter("some_custom_id"));
}
And don't forget to unregister it:
#Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mNotificationReceiver);
super.onDestroy();
}
When a broadcast is received, the code within the receiver is executed.
Now, to check if the app is in the foreground, and send a broadcast if it is. Inside of the IntentService:
#Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
String errorMessage = getErrorString(this,
geofencingEvent.getErrorCode());
return;
}
int geofenceTransition = geofencingEvent.getGeofenceTransition();
// Test that the reported transition was of interest.
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
...
if(MyApplication.isActivityVisible()){
Intent intnt = new Intent("some_custom_id");
intnt.putExtra("message", geofenceTransitionDetails);
LocalBroadcastManager.getInstance(this).sendBroadcast(intnt);
}else{
sendNotification(geofenceTransitionDetails);
}
} else {
// Log the error.
}
}
The important bit is the last nested if-statement:
if(MyApplication.isActivityVisible()){
Intent intnt = new Intent("some_custom_id");
intnt.putExtra("message", geofenceTransitionDetails);
LocalBroadcastManager.getInstance(this).sendBroadcast(intnt);
}else{
sendNotification(geofenceTransitionDetails);
}
Check if the app is in the foreground using MyApplication.isActivityVisible(), as defined above, and then either send the notification, or send a broadcast. Just make sure that your intent code (i.e. "some_custom_id") matches on your sender and receiver.
And that's about it. If the app is in the foreground (specifically the MainActivity), I execute some code. If the app is not in the foreground, I send a notification.
The easiest way would be to use LocalBroadcastManager or some event bus.
So when transition happens you should send local broadcast from IntentService and catch it with some component X in between IntentService and any of your Activity's. Component X must track if any of your Activity's is in foreground and
if yes - pass other local broadcast up (to the foreground Activity),
if not - show notification.
Please note that in Android you cannot track easily if your app is in foreground or not (and if you have more than 1 Activity, you cannot do it properly in my opinion) but you can try.
a) You can notify your service of the activity's lifecycle events.
b) You can keep the current state of your UI in a static field in the activity and check it from the service before showing the notification.

Resume singleTask activity

I am trying to "resume" a single task activity so it appears in the foreground when a user clicks my notification. (Same behavior as if the user tapped on the app icon from the applications menu.)
My notification creates a PendingIntent which broadcasts an action that is received by my broadcast receiver. If the app is in not in the foreground, I try to resume the app. Additionally, I'm trying to pass a message to my onResume function through the intent. However, I'm hitting an error:
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
Despite this error, my app is being resumed...don't understand why. However, my extras are not being passed to my onResume function.
So first I create a notification.
public static class MyNotificationCreator {
private static final int MY_NOTIFICATION_ID = 987;
public static void createNotification(Context context) {
Intent openAppIntent = new Intent(context, MyReceiver.class);
openAppIntent.setAction("PleaseOpenApp");
PendingIntent pi = PendingIntent.getBroadcast(context, /*requestCode*/0, openAppIntent, /*flags*/0);
Notification notification = ne Notification.Builder(context)
.setContentTitle("")
.setContentText("Open app")
.setSmallIcon(context.getApplicationInfo().icon)
.setContentIntent(pi)
.build();
NotificationManager notificationManager = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(MY_NOTIFICATION_ID, notification); }
}
Which broadcasts "PleaseOpenApp" for MyReceiver.
public class MyReceiver extends BroadcastReceiver {
#Override
public void onRecieve(Context context, Intent intent) {
if (intent.action() == "PleaseOpenApp" && !MyPlugin.isForeground) {
PackageManager pm = context.getPackageManager();
//Perhaps I'm not supposed to use a "launch" intent?
Intent launchIntent = pm.getLaunchIntentForPackage(context.getPackageName());
//I'm adding the FLAG_ACTIVITY_NEW_TASK, but I'm still hitting an error saying my intent does not have the FLAG_ACTIVITY_NEW_TASK...
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIntent.putExtra("foo", "bar");
context.startActivity(launchActivity);
} else {
//do other stuff
}
}
}
My plugin keeps track of whether or not we're in the foreground. Also, it tries to get "food" after my receiver attempts to start the app.
public class MyPlugin extends CordovaPlugin {
public static boolean isForeground = false;
#Override
public void initialize(CordovaInterface cordova, CordovaWebView webview) {
super.initialize(cordova, webview);
isForeground = true;
}
#Override
public void onResume(boolean multitasking) {
isForeground = true;
String foo = activity.getIntent().getStringExtra("foo");
Log.d("MyPlugin", foo); //foo is null after clicking the notification!
}
#Override
public void onPause(boolean multitasking) {
isForeground = false;
}
#Override
public void onDestroy() {
isForeground = false;
}
}
Note: because I'm using cordova my activity has a singleTask launchMode.
Also, I'm new to Android development so any help about resuming activities not in the foreground vs resuming activities that have been destroyed and info about general concepts / best practices that I'm not understanding would be appreciated!
I don't think your Broadcast/Broadcast Receiver pattern is necessary.
Intents can be used to directly launch an activity, and when you build the Intent, you can add the extras. Then, your activity onResume() can extract them directly.
Here is a sample Intent and PendingIntent construction that can be sent in a notification:
Intent startActivity = new Intent(context, MyActivity.class);
// You can experiment with the FLAGs passed here to see what they change
startActivity.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra("Extra1", myExtra1)
.putExtra("Extra2", myExtra2)
// ADDING THIS MAKES SURE THE EXTRAS ATTACH
.setAction("SomeString");
// Then, create the PendingIntent
// You can experiment with the FLAG passed here to see what it changes
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, startActivity, PendingIntent.FLAG_UPDATE_CURRENT);
// Then, create and show the notification
Notification notif = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.my_small_icon)
.setContentTitle(myTitle)
.setContentText(myContent)
.setOngoing(isOngoingNotif)
.setAutoCancel(shouldAutoCancel)
.setOnlyAlertOnce(shouldAlertOnce)
.setContentIntent(pendingIntent)
.build();
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
manager.notify(MY_NOTIFICATION_ID, notif);
In your code you are using a "launch Intent" to resume your application. You've added "extras" to the Intent but they will never be seen.
If your app is running, but in the background, and you call startActivity() with a "launch Intent", all this does it bring your task from the background to the foreground. It does not deliver the Intent to the Activity!.
A "launch Intent" does exactly the same thing as when you press the app icon of an app on the HOME screen (if it is already running, but in the background). This just brings the existing task in its current state, from the background to the foreground.
If you want to delivery "extras" to your app, you cannot use a "launch Intent". You must use a regular 'Intent. Depending on your architecture, you could either start a newActivity(which would get the "extras" inonCreate(), or you could start an existingActivity(which would get the "extras" inonNewIntent()`.

Cancel AsyncTask by clickig on Notification

I have an activity where I load different fragment with navigation drawer options.
Those fragments have different asynctasks (e.g. one for downloading image, one for importing for database etc.). Every time an asynctask is initiated, I use NotificationManager to show the progress.
What I am looking for is, if anybody click on any notification, it will cancel corresponding AsyncTask. I read about PendingIntent method, but I am not sure whether I need to open an intent to do that.
Also, I am confused how to get reference of the AsyncTasks from my MainActivity as they are initiated inside fragment (and those fragments get destroyed time to time).
I can put some code here if you want, but the code is very basic AsyncTask and Fragment based Navigation Drawer with single Activity.
Thanks,
public class MyFragment1 extends Fragment {
private DownloadFile asynctaskhandler;
public method(){
asynctaskhandler = new DownloadFile();
asynctaskhandler.execute();
}
private class DownloadFile extends AsyncTask<Void, String, Void> {
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
int mId;
protected Void doInBackground(Void... args) {
while(){
//DON SOMETHING, GET PROGRESS progress
mBuilder.setProgress(mId, progress, true);
mNotifyManager.notify(mId, mBuilder.build());
}
}
}
}
To many questions in this one... I'll try to answer the one about notification.
notification:
Intent intent2 = new Intent();
intent2.setAction("com.app.example.MyServiceClass.STOP");
PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, intent2, 0);
noti = new NotificationCompat.Builder(this)
.setContentTitle("Recorder")
.setContentText("running")
.setSmallIcon(R.drawable.ic_launcher)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.addAction(R.drawable.ic_launcher, "Stop", pIntent)
.build();
startForeground(12345, noti);
Service (receiver of the pendingIntent):
// registering BroadcastReceiver
IntentFilter filter2 = new IntentFilter();
filter2.addAction("com.app.example.MyServiceClass.STOP"); //further more
registerReceiver(receiver, filter2);
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals("com.app.example.MyServiceClass.STOP")){
isCancelled = true; // this is a class variable
sendMessage("hide");
}
}
};
doInBackground:
protected Boolean doInBackground(String... StringUrls) {
// some loop
if (myServiceClass.this.isCancelled) {
myServiceClass.this.stopSelf();
}
// some loop
}

Categories

Resources