I have a foreground service with a notification. When the user clicks the notification, it will bring the user back to the main screen viewmodel (or load it again if unloaded).
I want to add 2 actions (pause, stop) to the notification. It is preferred to call the same viewmodel with a parameter to indicate the action type, and let the main screen viewmodel to handle the action. As the main screen viewmodel may have been loaded already, no more initialization will be executed. Passing parameter like showing viewmodel does not work. The existing viewmodel does not know it was triggered from the notification indeed.
How can I pass a different parameter for each action type to the viewmodel, and retrieve it in the viewmodel to act accordingly? Or it should be done in a different way?
This is the code of the notification:
var request = MvxViewModelRequest<RouteLayoutViewModel>.GetDefaultRequest();
var intent = Mvx.Resolve<IMvxAndroidViewModelRequestTranslator>().GetIntentFor(request);
const int pendingIntentId = 0;
PendingIntent pendingIntent = PendingIntent.GetActivity(Application.Context, pendingIntentId, intent, PendingIntentFlags.UpdateCurrent);
var builder = new NotificationCompat.Builder(this);
builder
.SetContentTitle(AppConstants.AppName)
.SetContentText("notification_text")
.SetSmallIcon(Resource.Drawable.Icon)
.SetContentIntent(pendingIntent); ..........
var notification = builder.Build();
StartForeground(AppConstants.SERVICE_RUNNING_NOTIFICATION_ID, notification);
Thanks,
Nick
MvvmCross 5 changed the way the ViewModel parameters are serialized internally. MvvmCross 5 hasn't been updated yet to handle this scenario AFAIK. Here is some sample code demonstrating how to workaround the issue using a BroadcastReceiver currently. This approach will allow you to navigate with parameters after the user clicks on the Android Notification.
When you're building the notification:
var intent = new Intent(context, typeof(YourBroadcastReceiver));
intent.SetAction("YOUR_ACTION");
// Put your other parameters here...
intent.PutExtra("id", id);
var pendingIntent = PendingIntent.GetBroadcast(context, _notificationId, intent, PendingIntentFlags.UpdateCurrent);
...
notificationBuilder.SetContentIntent(pendingIntent);
Then you add a BroadcastReceiver class like so:
[BroadcastReceiver]
public class YourBroadcastReceiver : MvxBroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action == "YOUR_ACTION")
{
// TODO: Extract the contents from the intent.
var id = intent.GetIntExtra("id", 0);
// TODO: Navigate using IMvxNavigationService using the parameters pulled from the Intent.
}
}
}
Related
I want some methods to execute when I click on Notification Action Button.
I have searched on this site, but everything seems to be in order and my IntentService is not being called.
My Action-Button Intent
Intent off = new Intent();
off.setAction("action");
off.putExtra("test", "off");
PendingIntent pOff = PendingIntent.getService(context, 22, off, 0);
Notification Builder
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(/**/)
.setContentTitle(/**/)
.setContentText(/**/)
.addAction(/**/, "Off", pOff)
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true);
Intent Service Class
public class NotificationServiceClass extends IntentService {
public NotificationServiceClass(String name) {
super(name);
}
public NotificationServiceClass () {
super("NotificationServiceClass");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i("test", "onHandle");
if (intent.getAction().equals("action")) {
Log.i("test", "action");
Bundle bundle = intent.getExtras();
if (bundle != null) {
Log.i("test", "onHandleBundleNotNull");
if (bundle.containsKey("test")) {
Log.i("test", bundle.getString("test"));
}
}
}
}
}
XML Declaration for Service class
<service
android:name=".Manager.NotificationServiceClass"
android:exported="false">
</service>
Per the Intents and Intent Filters training, the Intent you've built is an implicit Intent:
Implicit intents do not name a specific component, but instead declare a general action to perform, which allows a component from another app to handle it. For example, if you want to show the user a location on a map, you can use an implicit intent to request that another capable app show a specified location on a map.
What you actually want is an explicit intent: one that specifies the component to start by name as per the note on the same page:
Note: When starting a Service, you should always specify the component name. Otherwise, you cannot be certain what service will respond to the intent, and the user cannot see which service starts.
When constructing your Intent, you should use
// Note how you explicitly name the class to use
Intent off = new Intent(context, NotificationServiceClass.class);
off.setAction("action");
off.putExtra("test", "off");
PendingIntent pOff = PendingIntent.getService(context, 22, off, 0);
In looking at your code, I do not see you telling the PendingIntent what class to use for your service.
You should add:
off.setClass(this, NotificationServiceClass.class);
Otherwise the PendingIntent has nothing to do.
I have a problem with notification on Android (Xamarin).
My scenario is this:
I have a class handling (inheriting GcmServiceBase) the message and creating the Notification object.
In this class I override OnMessage method in this way:
protected override void OnMessage(Context context, Intent intent)
{
if (intent != null || intent.Extras != null)
{
string messageText = intent.Extras.GetString("message");
string messageTitle = intent.Extras.GetString("title");
Intent app_launch_intent = new Intent(context, typeof(Project.WaitForm));
if (App.Instance == null)
{
Console.WriteLine("GCM: Notification received while application not running...");
app_launch_intent.AddFlags(ActivityFlags.ClearTop);
app_launch_intent.AddFlags(ActivityFlags.SingleTop);
}
else if ((App.Instance != null) && (App.Instance.mainActivity.IsInBackground))
{
App.Instance.Logger.Write("GCM: Notification received while application in background...", LogType.Default, LogLevel.Info);
app_launch_intent = new Intent(context, App.Instance.mainActivity.GetType());
}
else
{
App.Instance.Logger.Write("GCM: Notification received while application in foreground...", LogType.Default, LogLevel.Info);
app_launch_intent = new Intent(context, App.Instance.mainActivity.GetType());
app_launch_intent.AddFlags(ActivityFlags.SingleTop);
}
app_launch_intent.PutExtras(intent.Extras);
app_launch_intent.PutExtra("isNotify", true);
PendingIntent pendingIntent = PendingIntent.GetActivity(this, PushService.notificationId, app_launch_intent, PendingIntentFlags.OneShot);
createNotification(context, app_launch_intent, pendingIntent, messageTitle, messageText);
if (App.Instance == null)
{
Console.WriteLine("GCM: Notification object correctly created.");
}
else
{
App.Instance.Logger.Write("GCM: Notification object correctly created.", LogType.Default, LogLevel.Info);
}
}
}
public void createNotification(Context context, Intent result_intent, PendingIntent pendingIntent, string title, string desc)
{
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
Notification.Builder builder = new Notification.Builder(this)
.SetContentIntent(pendingIntent)
.SetAutoCancel(true)
.SetContentTitle(title)
.SetContentText(desc)
.SetSmallIcon(Resource.Drawable.notify_icon_transparent)
.SetLargeIcon(PushService.IconAgenda)
.SetTicker(title);
Notification notification = builder.Build();
PushService.notificationId += 1;
notificationManager.Notify(PushService.notificationId, notification);
}
as you can see, when message is received I test if application object exists then I create Intent and notification object (App.Instance is a singleton holding informations about the app itself. mainActivity is the current activity shown on screen).
In this way when notification is clicked, last activity on the top of the stack (current on-screen activity if app is in foreground or last on-screen activity before app went in background) will come
up and something in it will take care about notification itself, called by OnNewIntent.
Everything is working fine except for a thing:
Let's say I have 3 Activity called A, B, C.
I start the app and then I receive 2 different notifications when current activity on screen is A.
Both will be displayed in the top bar without problems.
I click on the first of them and it asks me to open another activity (catched in OnNewEvent of current activity and due to notification type), so I say "yes" and I will have a transition to activity C.
Once reached activity C and after done some work, I click on the other notification.
Clicking on it I will be carried to activity A and then notification will be handled.
I know this "problem" is because at notify creation I use the App.Instance.mainActivity.GetType() to create intent that will be use to create PendingIntent and that at that time was activity A for both.
So my question is:
How can I handle second notify click in last activity on screen (so in activity C) instead of activity A?
(I add Xamarin tag just because code above is in C# and not in Java so it may sound strange to a native Android developer)
I want to reset a variable of my service when user clears my notification: that's all!
Looking around I see that everyone suggest to add a delete intent on my notification, but intent is used to start an activity, a service o whatever while I just need a thing like this:
void onClearPressed(){
aVariable = 0;
}
how to obtain this result?
Notifications are not managed by your app and all things like showing notifications and clearing them are actually happening in another process. You can't make another app directly execute a piece of code just because of security reasons.
The only possibility in your case is to provide a PendingIntent which just wraps around a regular Intent and will be started on behalf of your app when notification is cleared.
You need to use PendingIntent for sending broadcast or starting a service and then doing what you want in the broadcast receiver or in the service. What exactly to use depends on from which application component you are showing notifications.
In case of broadcast receiver you can just create an anonymous inner class for broadcast receiver and register it dynamically before showing notification. It will look something like that:
public class NotificationHelper {
private static final String NOTIFICATION_DELETED_ACTION = "NOTIFICATION_DELETED";
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
aVariable = 0; // Do what you want here
unregisterReceiver(this);
}
};
public void showNotification(Context ctx, String text) {
Intent intent = new Intent(NOTIFICATION_DELETED_ACTION);
PendingIntent pendintIntent = PendingIntent.getBroadcast(ctx, 0, intent, 0);
registerReceiver(receiver, new IntentFilter(NOTIFICATION_DELETED_ACTION));
Notification n = new Notification.Builder(mContext).
setContentText(text).
setDeleteIntent(pendintIntent).
build();
NotificationManager.notify(0, n);
}
}
Andrei is correct.
If you want multiple messages back such as:
you want to know if the message was clicked
you attached an action with an icon that you want to catch
AND you want to know if the message was canceled
you must register each of those response filters:
public void showNotification(Context ctx, String text) ()
{
/… create intents and pending intents same format as Andrie did../
/… you could also set up the style of your message box etc. …/
//need to register each response filter
registerReceiver(receiver, new IntentFilter(CLICK_ACTION));
registerReceiver(receiver, new IntentFilter(USER_RESPONSE_ACTION));
registerReceiver(receiver, new IntentFilter(NOTIFICATION_DELETED_ACTION));
Notification n = new Notification.Builder(mContext)
.setContentText(text)
.setContentIntent(pendingIntent) //Click action
.setDeleteIntent(pendingCancelIntent) //Cancel/Deleted action
.addAction(R.drawable.icon, "Title", pendingActionIntent) //Response action
.build();
NotificationManager.notify(0, n);
}
Then you can catch the different responses with if, else statements (as Andrei did), or with a switch statement.
Note: I make this response primarily because I could not find this anywhere, and I had to figure it out on my own. (perhaps I will remember it better for that :-) Have Fun!
I have an application with two buttons. One button that "closes" the application and one that begins the algorithm. When I click "begin" it "hides" the application and displays a notification in the notification bar. I need to be able to execute/call a method when the notification is clicked/pressed. There are a few answers for this sort of question, but they are incredibly vague and one only points to a link to the doc on BroadcastReceiver.
If you are going to leave a url to the BroadcastReceiver doc and say "read this page," please don't reply to this question. If you are going to explain how I could use BroadcastReceiver to execute a method (from within the same class that displayed the notification), please show me some code for how this could be done.
My algorithm: press a button, display notification, click notification, call a method (don't display activity). That's it.
If it's not possible, just let me know. If it is, please show me what you would do to make it possible. Something this simple shouldn't have been overlooked by the developers of the android sdk.
After several iterations of trial and error, I finally found a fairly straightforward and clean way to run an arbitrary method when a notification's action is clicked. In my solution, there is one class (I'll call it NotificationUtils) that creates the notification and also contains an IntentService static inner class that will run when actions on the notification are clicked. Here is my NotificationUtils class, followed by the necessary changes to AndroidManifest.xml:
public class NotificationUtils {
public static final int NOTIFICATION_ID = 1;
public static final String ACTION_1 = "action_1";
public static void displayNotification(Context context) {
Intent action1Intent = new Intent(context, NotificationActionService.class)
.setAction(ACTION_1);
PendingIntent action1PendingIntent = PendingIntent.getService(context, 0,
action1Intent, PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Sample Notification")
.setContentText("Notification text goes here")
.addAction(new NotificationCompat.Action(R.drawable.ic_launcher,
"Action 1", action1PendingIntent));
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
}
public static class NotificationActionService extends IntentService {
public NotificationActionService() {
super(NotificationActionService.class.getSimpleName());
}
#Override
protected void onHandleIntent(Intent intent) {
String action = intent.getAction();
DebugUtils.log("Received notification action: " + action);
if (ACTION_1.equals(action)) {
// TODO: handle action 1.
// If you want to cancel the notification: NotificationManagerCompat.from(this).cancel(NOTIFICATION_ID);
}
}
}
Now just implement your actions in onHandleIntent and add the NotificationActionService to your manifest within the <application> tags:
<service android:name=".NotificationUtils$NotificationActionService" />
Summary:
Create a class that will create the notification.
Inside that class, add a IntentService inner classes (make sure it is static or you will get a cryptic error!) that can run any method based on the action that was clicked.
Declare the IntentService class in your manifest.
On Notification click we can't get any fire event or any click listener. When we add notification in notification bar, we can set a pending intent, which fires an intent (activity/service/broadcast) upon notification click.
I have a workound solution for you, if you really don't want to display your activity then the activity which is going to start with pending intent send a broad cast from there to your parent activity and just finish the pending activity and then once broadcast receiver receives in parent activity call whatever method you want inside the receiver. For your reference..
// This is what you are going to set a pending intent which will start once
// notification is clicked. Hopes you know how to add notification bar.
Intent notificationIntent = new Intent(this, dummy_activity.class);
notificationIntent.setAction("android.intent.action.MAIN");
notificationIntent.addCategory("android.intent.category.LAUNCHER");
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT |
Notification.FLAG_AUTO_CANCEL);
// Now, once this dummy activity starts send a broad cast to your parent activity and finish the pending activity
//(remember you need to register your broadcast action here to receive).
BroadcastReceiver call_method = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action_name = intent.getAction();
if (action_name.equals("call_method")) {
// call your method here and do what ever you want.
}
};
};
registerReceiver(call_method, new IntentFilter("call_method"));
}
}
I got a Activity which creates an alarm.
The alarm calls a Broadcast Receiver. In on Receive i create a Notification with the extras from the Activity (ID, Title, Content). The Alarm triggers the Broadcast Receiver creates the notification well.
But when i re install the application or install a newer version and setup a new Alarm with a new title and content the receiver shows me the first create notification intent. i can create may Alarm triggers all works but they show always the first create Notification intents.
I use a internal application counter for creating a notification ID
public class CollectApplication extends Application {
private Integer reminderCount;
#Override
public void onCreate() {
reminderCount = 1;
super.onCreate();
}
public Integer getReminderCount() {
return reminderCount;
}
public void setReminderCount(Integer reminderCount) {
this.reminderCount = reminderCount;
}
}
Of course after re installing or updating the application the counter starts from 1. But i create a new intent with the same ID 1 so i override it right?
How to override it or remove the intent from the notification to create a new one with new extras to display?
Or Should i save the current ID in the shared preferences?
How to override it or remove the intent from the notification to create a new one with new extras to display?
Use FLAG_UPDATE_CURRENT when you create your PendingIntent for the new Notification.
Or Should i save the current ID in the shared preferences?
No, because you should only have one ID. You may wish to persist your reminder count, though.
->Just add PendingIntent.FLAG_UPDATE_CURRENT
Example:
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);