Android: Don't open app after performing notification on-click action - java

The main part of my app is a service that displays a notification. The GUI of the app is only used to be able to change some settings. When I click on that notification, some data should be send in the background without opening the app. To do that, I created an invisible activity.
When I open the app (the configuration part) and exit it with the back button, everything works as intended; when clicking on the notification the data is send without opening the app.
When I exit the app with the home button, every time I click the notification the app opens again.
This is my notification and the Intent to call the data activity:
PendingIntent toggleLightsIntent =
PendingIntent.getActivity(
this,
0,
new Intent(this, HyperionToggleSwitchActivity.class),
PendingIntent.FLAG_NO_CREATE);
Notification notification = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("HyperionSwitch")
.setContentText("Switch lights")
.setContentIntent(toggleLightsIntent)
.build();
And this is the part where I send the data:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sendJson(blue);
finish();
}
What do I have to change so the app won't open when clicking the notification?
It's my first app and took me quite a while to get what I have so far...

As per earlier comment:
You could change your hidden activity into a Service (which generally doesn't have any ui), or leverage the service that manages the notification, and have the PendingIntent sent directly to that service using PendingIntent.getService() .

Try this code^
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(MainActivity.FILE_NAME, "somefile");
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

Related

Opening Android app using notification & getLaunchIntentForPackage doesn't go through LauncherActivity

I am using Firebase (FCM) to show Push Notifications to the user and I am running into a weird problem.
The code I have works for the following scenarios (using FirebaseMessagingService):
App in foreground - Receiving data in onReceive() and showing a popup inside app.
App in background - Receiving data in onReceive() and showing a notification for the user. If this is clicked the app will be brought back to front. The intent from this is received in LauncherActivity followed by a finish() call which takes me to whatever activity I already had open.
App completely closed - same as background. App will be started and intent will be handled in LauncherActivity before calling finish() on that.
And here is where it gets interesting:
App completely closed -> open it through notification (intent received in LauncherActivity) -> put the app in background and send another notification -> when this notification is clicked the LauncherActivity is completely ignored (onCreate is no longer called) and I get taken straight to whatever activity I already had. The intent here has no extras or categories.
Why is LauncherActivity being bypassed in this specific case? Keep in mind that this works fine if the app was initially started normally (not by clicking on a notification)
Intent mainIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (mainIntent != null) {
mainIntent.addCategory(NOTIFICATION_CATEGORY);
mainIntent.putExtra(.........);
}
PendingIntent pendingMainIntent = PendingIntent.getActivity(this, SERVICE_NOTIFICATION_ID, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, context.getString(R.string.default_notification_channel_id));
notificationBuilder.setContentIntent(pendingMainIntent);
//.....icon, color, pririty, autoCancel, setDefaults, setWhen, setShowWhen, contentText, setStyle
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
getString(R.string.default_notification_channel_id),
getString(R.string.default_notification_channel),
NotificationManager.IMPORTANCE_HIGH
);
notificationManager.createNotificationChannel(channel);
notificationBuilder.setChannelId(getString(R.string.default_notification_channel_id));
}
notificationManager.notify(SERVICE_NOTIFICATION_ID, notificationBuilder.build());
}
I'd appreciate any ideas. Thank you.
When you launch an app for the first time, Android remembers the Intent that was used to launch it. Normally, when you launch an app from the HOME screen, this is an Intent that contains ACTION=MAIN and CATEGORY=LAUNCHER. If your app then goes to the background (for whatever reason), and the user later taps the icon on the HOME screen, the same launch Intent is used. Android matches this against the Intent used to launch the app for the first time, and if these match, Android doesn't launch a new Activity, it just brings the task containing the app from the background to the foreground in whatever state it was in when it got moved to the background. Under normal circumstances, this is exactly the behaviour that you want (and that the user expects).
However, when the app is launched for the first time from a Notification, this can mess things up. In your case, this is what you are seeing. You launch the app from a Notification and Android remembers the Intent used (from the Notification), when you later launch the app (again from a Notification), android matches the Intent in the Notification with the Intent used to launch the app for the first time, and thinks you want to bring the existing app task from the background to the foreground.
There are several ways to deal with this, depending on the behaviour that you want to have. The best thing to do is probably not to launch your root Activity (the one with ACTION=MAIN and CATEGORY=LAUNCHER) from the Notification. Instead launch a different Activity and have that Activity determine what it should do next (ie: redirect to the root Activity or something else, depending on the state of your app). You should also set the NO_HISTORY and EXCLUDE_FROM_RECENTS flags on the Intent that you put in the Notification. This will ensure that Android won't remember this Intent as the one that launched the app.

How to ask user to unlock device on clicking notification action in android?

I am displaying a notification from my application and this notification has an action in it, when user clicks on the action, the corresponding action class is called with the intent I set. Now, I want to perform a particular action but before that the user needs to unlock the screen if it is pin/pattern protected. I am not able to ask user to unlock device, i.e open up the unlock keypad/pattern on lock screen.
Below is the code I have,
//HandleAction is a java class that extends IntentService
Intent intent = new Intent(context, HandleAction.class);
intent.putExtra(key, "my_value"); //Used to send information to action class
PendingIntent pi = PendingIntent.getService(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notification = new NotificationCompat.Builder(mContext);
//set the title, icon etc for builder and add action as below
notification.addAction(icon, "my_label", pi);
When user clicks on the notification action, I get the control to onHandleIntent in MyAction.java
In here, I want to request user to unlock device if password protected and then perform an action.
How can I request user to unlock device in onHandleIntent?
I came across using KeyguardManager and KeyguardLock to acheive this but keyguardManager.newKeyguardLock is deprecated method and I want to avoid this. So, the next was using "FLAG_TURN_SCREEN_ON" and "FLAG_KEEP_SCREEN_ON" but I am unable to figure out how to use them in this context. I don't launch any window from my action class, it is just an operation like incrementing my counter. After clicking it the notification should disappear, perform my action and thats it.
I found a similar question Unlock phone , but the way it was did is by launching a dummy/empty activity.
Thanks in advance for any help, suggestions :)
In the Activity you are opening (that is behind the lock screen) you should tell system to lift the keyguard (e.g. in onCreate())
For API >= 26
KeyguardManager km = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
km.requestDismissKeyguard(this, null); // you may add callback listener here
For API < 26:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
If device is not locked securely it would unlock it immediately, and if it is locked, it will ask user to do it.
Use Activity intent in pending intent insted of Service
Intent intent = new Intent(context, MyActivity.class);
intent.putExtra(key, "my_value"); //Used to send information to action class
PendingIntent pi = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);

Use notification as a toggle

I'm currently developing a simple Flashlight app.
I want to have a notification which can be pressed by the user and works like a toggle for the flashlight, so the user can turn the flashlight on and off, even if the app runs in the background and isn't visible to the user.
I already experiemented a lot with notifications, but the only thing I managed to achieve was a notification that started an activity which was then brought to the foreground.
As I understand it, notifications can only launch activities. So how can I achieve that the activity stays in the background and just turns the flashlight on/off?
As I understand it, notifications can only launch activities.
You are wrong. You can start a service with PendingIntent.getService.
Intent notificationIntent = new Intent(mContext, HandleNotificationClickService.class);
PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, notificationIntent, 0);
Source: Answer to "Start Service from Notification"
And if I understand you correctly, you probably also want to use Builder.setOngoing to keep your notification visible after click.

Resuming current background activity on notification

My application contains multiple activities. I have implemented push notifications and also shown the notification in bar. My issue is, when i click on notification is take me to the specific activity that i has specified.
Intent intent =new Intent(GcmService.this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(GcmService.this, 0, intent, 0);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
getApplicationContext())
.setContentTitle(getResources().getString(R.string.app_name))
.setAutoCancel(true)
.setContentIntent(pendingIntent);
notificationBuilder.setContentIntent(pendingIntent);
mNotificationManager.notify((int) when, notificationBuilder.build());
I want if my activity is in background, and user click on the notification app resume the current activity that is in background and show dialog box.
And if my application is closed. open the Launching activity and then show the dialog box.
If you want to continue again when you click the notification(in this case your application still running on background) than you can using this method :
http://developer.android.com/training/basics/activity-lifecycle/stopping.html
I've tried it and it works.
You can link your notification to a DispatcherActivity.
If you have open Activities on the backstack, finish the DispatcherActivity suddenly in onCreate(). If not, forward to your launching activity and finish the DispatcherActivity too.
To track your active activities on backstack use this How to know Activity Count of my application? suggestions.

Android: How to avoid that clicking on a Notification calls onCreate()

In my application I notify the user with notifications, if something special happens:
public void triggerNotification(String msg) {
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Intent contentIntent = new Intent(this, ABC.class);
Notification notification = new Notification(R.drawable.icon, msg, System.currentTimeMillis());
notification.setLatestEventInfo(this, "ABC", msg, PendingIntent.getActivity(this.getBaseContext(), 0, contentIntent, PendingIntent.FLAG_CANCEL_CURRENT));
notification.flags = Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(notificationCounter, notification);
notificationCounter++;
}
If the user clicks on the Notification, the onCreate() method is called. But I want that a specific method in my app is called, or if the app is not in the foreground, that it is brought back to the foreground.
I know there are lots of tutorials that explain how to handle notifications, but I just don't understand them completely and wasn't ever able to implement the things like I'd like to.
To bring your app to the foreground if it is running already you need to set different flags on your intent:
contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
For running a specific method you could just pass extra information along with the intent and interpret it in your application to decide which method to run.
The recommendation to use FLAG_ACTIVITY_CLEAR_TOP and FLAG_ACTIVITY_SINGLE_TOP only partially solves the problem. The activity in the Android manifest should also have these settings applied so that launching the activity from the home screen has the same behavior. Without these properties multiple instances of the activity can be launched.
<activity android:name="foo"
android:clearTaskOnLaunch="true"
android:launchMode="singleTop"
android:label="#string/app_name">
I've discovered that if you use Intent contentIntent = new Intent(this, ABC.class); this calls onCreate(); regardless of the flags set.
Use Intent contentIntent = getIntent(); to skip onCreate(); and that moves to onStart();

Categories

Resources