Android pending intents issue and global class - android

So, my app, has multiple activities.
One of them displays a list of messages (retrieved from a local SQLite).
I also have a GlobalClass that extends Application and I use it in order to store some values between activities.
Like this:
import android.app.Application;
public class GlobalClass extends Application {
private String uzid;
...
public String getUzer() {
return uzid;
}
public void setUzer(String aUzer) {
uzid = aUzer;
}
My app, of course has a login page, where user authenticates itself before gaining access to the app. When he does that, I set a userId integer in my GlobalClass with the id number of the logged user.
Until now, nothing fancy.
I also have a sync adapter that checks for new messages on a remote server and synchronizes the 2 tables (local and remote). And when the sync adapter finds new messages on the remote server, it generates an Android Notification. When user clicks on the notification, my app opens and the activity with the inbox shows up displaying all messages.
I noticed that from time to time, my globalClass userID is empty, even though when I start the app, you cannot get passed the login activity without setting it.
So the app absolutelly sets this globalClass value.
So my guess is that when my android notification is clicked, and the activity with the message list is shown, somehow the globalClass is re-initialized ?!?!?!?!
Is there any way of making sure this does not happen?
I really need my user to be able to use the app just like never happened when he starts it from the notification bar by clicking on the notification.
I mean, the user logs in, GlobalClass userId is being set, then the app goes on. After a a while it enters pause, display shuts down, then a new message arrives and a notification is shown. The user clicks it and my app is brought back to the display showing messages, but the userId is null now.
Why?
And how to prevent this?
This is how I show the notification when a new message is found in the syncAdapter:
Intent intent = new Intent(context, MesajeActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, i, intent, PendingIntent.FLAG_UPDATE_CURRENT); //(context, 0, intent, 0);
// Build notification
Notification noti = new Notification.Builder(context)
.setContentTitle("Message from "+ self.getSursanume())
.setContentText(self.getTitlu()).setSmallIcon(R.drawable.new_50)
.setContentIntent(pIntent)
.build();
NotificationManager notificationManager;
notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
// hide the notification after its selected
noti.flags |= Notification.FLAG_AUTO_CANCEL;
noti.defaults |= Notification.DEFAULT_VIBRATE;
noti.sound = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.htc4);
noti.number = i;
notificationManager.notify(i, noti);
What is wrong here?
I use the current user id (found in GlobalClass) to identify the messages that are intended for the current user, so in onCreate, and also in onResume I instantiate my GlobalClass and retrieve the userID. But this crashes my app. Because sometimes when entering the app from the notification, my global variable "userID" is NULL.
How do I make this problem go away?
Thank you

I noticed that from time to time, my globalClass userID is empty, even though when I start the app, you cannot get passed the login activity without setting it. So the app absolutelly sets this globalClass value.
First, your app may not be started to launch an activity (e.g., app started to send a command to a service).
Second, your app may not be started to launch your login activity (e.g., user came back to you from recent-tasks list).
So my guess is that when my android notification is clicked, and the activity with the message list is shown, somehow the globalClass is re-initialized ?!?!?!?!
Your process was terminated in between invocations. This is perfectly normal.
Is there any way of making sure this does not happen?
Not really. Processes go away all the time, on all operating systems, for any number of reasons (e.g., crashes, manual termination by the user).
I really need my user to be able to use the app just like never happened when he starts it from the notification bar by clicking on the notification.
Then you are going to need to persist your data. Static data members, including custom Application objects, are only a cache.
Or, rather than having a "login activity", have it be some sort of fragment that you can display in any of your activities when authentication is needed.
Or, have your Notification route to the "login activity", which would determine that authentication is not needed and forwards the user along to the end destination. If, OTOH, authentication is needed, the "login activity" handles this, then forwards the user along to the end destination.

Related

Notification click behaviors depending on the state of the app

After a long search, I was not able to find exactly what I need.
I simply want : When a notification is displayed to the user, if the app is simply in background I want to reopen MainActivity. But if the app/activity has been killed, I want to restart the app completely.
My actual code :
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(notification.getSubject())
.setContentText(notification.getMessage())
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
mBuilder.setContentIntent(
PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_UPDATE_CURRENT));
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notif = mBuilder.build();
notif.flags = Notification.DEFAULT_VIBRATE | Notification.FLAG_AUTO_CANCEL;
Case which is not working actually is to restart the app if app or activity has been killed.
EDIT : By killed I mean the app has been closed in the app manager. Or we lost the focus and the activity is destroyed. In those cases I would like to fully restart the app.
I think you don't entirely understand how android functions. Whenever your program is needed, the program is started by android (See the Application class for an onCreate that you can override).
Whenever an activity is necessary it is (re)created or brought to the foreground.
That means that an activity restart does not necessarily imply a restart of the application. In practice, if the program was still in memory, then an application restart won't happen.
If you want to investigate this further
create an Application.onCreate method so you see whether your app is restarted or not.
trigger the notification
go to the android settings, application tab, and there select 'show cached processes'. Clear your process from the list.
tap the notification.
This should trigger a restart of the application. If this is indeed the problem then you cannot 'solve' it. Android decides when it will kill your application and when it removes it from memory.
The best solution then is to redesign your activity that whatever the application restart would trigger is also performed in the activity itself. Or so, without more detail what functionality you would like to see performed on an 'activity/application-restart' it is difficult to advice further on this matter.
I think you need to change your setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) value to setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT).
The reason is due to the way that FLAG_ACTIVITY_REORDER_TO_FRONT
works. When you use this flag, Android looks for an instance of the
desired activity in your activity stack, starting from the front of
the stack and scanning until it gets to the root/back of the stack. As
soon as it finds an instance of the specified activity, it brings that
one to the front (ie: if there are multiple instances of the specified
activity it will bring to the front the most recent instance).
Original I found here.
See android developer doc here.
I hope its help you.

How to define started activity when relaunching Android application?

I am fairly new to Android, and am currently working on a simple XMPP Client. A user should be able to log in, and should be notified whenever an XMPP message arrives. It should be possible to access an overview of all messages that arrived during the current session.
When launching the application, a LoginActivity is started, prompting the user to fill in his or her credentials. If the right credentials are provided, a background service is started:
Intent intent = new Intent(this, NotificationService.class);
startService(intent);
On startup, the notification service adds a packet listener to the XMPP connection and requests to be running in the foreground. The users is prompted with a notification caused by this foreground request ("Ongoing"). Now I have provided a second activity called XMPPClientActivity, showing all messages that are received during the session and a simple logout button. When opening the application from within the "Ongoing" notification, the XMPPClientActivity is started because the notification is defined like this:
xmppIntent = new Intent(this, XMPPClientActivity.class);
pendingIntent = PendingIntent.getActivity(this, 0, xmppIntent, 0);
NotificationCompat.Builder xmppBuilder = new NotificationCompat.Builder(this);
xmppBuilder.setContentIntent(pendingIntent);
// Notification details
startForeground(id, xmppBuilder.build());
When opening the application from the home screen however, the LoginActivity is opened again. Of course I want the XMPPActivity to be started, but I can't seem to figure out how this should be done. I have been looking into binding an activity to a service, but I'm unsure if this can be of any help. What is the right way to do this?
What you can do, as I understand you issue, is use ShaeredPreferences. Create a preference like "loggedin" and set a boolean variable to true the first time they log in. Now you can set this to false when they click the "logout" Button.
When the Activity is started you can check the SharedPreference before calling setContentView() and if the value is true then finish() the LoginActivity and open your other Activity.
The link to the docs I provided has a good example of creating, opening, and editing SharedPreferences

multiple call to startforeground?

i have created a service (EmailService) that sends email ... each time i need to send an email with my app, it starts the service and pass the id of the email via an intent...
i am using startforeground(id_of_email, mynotifcation); to prevent it from being killed and to show a notification to the user of the status of the email sending.
i need to allow the user to send multiple emails at the time, so when the user needs to send another email, it again calls startservice with a new intent(different id of email)...so it calls startforeground(new_id_of_email, mynotifcation); again.
the problem is that the new call to startforeground overwrites the previous notification... (so the user loses the previous notification and doesn't know what is going on with his previous email)
Looking at the Service.startForeground() source shows that multiple calls to startForeground will only replace the currently shown notification. In fact, the call to startForeground is identical to stopForeground(), only with removeNotification set always set to true.
If you wish for you service to display a notification for each email in progress, you will have to manage each notification individually from the service.
public final void startForeground(int id, Notification notification) {
try {
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, id,
notification, true);
} catch (RemoteException ex) {
}
}
public final void stopForeground(boolean removeNotification) {
try {
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, 0,
null, removeNotification);
} catch (RemoteException ex) {
}
}
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r1/android/app/Service.java#Service.startForeground%28int%2Candroid.app.Notification%29
One can also use STOP_FOREGROUND_DETACH flag.
Quoting from the documentation :
STOP_FOREGROUND_DETACH
added in API level 24 int STOP_FOREGROUND_DETACH Flag for
stopForeground(int): if set, the notification previously provided to
startForeground(int, Notification) will be detached from the service.
Only makes sense when STOP_FOREGROUND_REMOVE is not set -- in this
case, the notification will remain shown, but be completely detached
from the service and so no longer changed except through direct calls
to the notification manager.
Constant Value: 2 (0x00000002)
So, before a repeated call to startForeground() you can call stopForeground(STOP_FOREGROUND_DETACH);. This will detach the notification and repeated calls to startForeground() will not make modifications to it, if you use a different notification-id.
Further, the "detached" notification, now, does not represent the "ongoing service" and hence can be removed by user with a swipe.
BONUS :
For compatibility, one can use ServiceCompat class and its static method ServiceCompat.stopForeground(MyService.this, STOP_FOREGROUND_DETACH) as is documented here.
I created a utility class to manage foreground service notification(s) based on #zeekhuge's answer. You can find the snippet here: https://stackoverflow.com/a/62604739/4522359

Is it possible to remove/cancel a notification after the user sees it?

I am currently making an application that uses Notifications.
I was able to display the notifications, and remove them from the notification list when the user taps them. However, I would also like my notifications to disappear if the user sees them but does not act on them.
For instance, the user displays the notification list, then taps on a notification that is not mine, or just closes the notification list. During those cases, I am trying to make my notifications get canceled and not displayed the next time the user displays the notification list even if he did not do anything to my notification.
Is this possible?
Thanks! :D
(edit: if you are wondering why I thought of this, a very simplified explanation would be: think of the notification as a toast instead; a toast that has a longer existence, and makes sure that the user actually saw it before disappearing.)
you may remove a notification by providing its id to the cancel method:
((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(your_notification);
however, i wouldn't recommend you to do it as it may confuse the user
It is very easy, there is a cancel method for notifications:
public void cancel (int id)
Cancel a previously shown notification. If it's transient, the view will be hidden. If it's persistent, it will be removed from the status bar.
public void cancel (String tag, int id)
Cancel a previously shown notification. If it's transient, the view will be hidden. If it's persistent, it will be removed from the status bar.
public void cancelAll ()
Cancel all previously shown notifications. See cancel(int) for the detailed behavior.
Details are here:
http://developer.android.com/reference/android/app/NotificationManager.html#cancel(int)
Update
If you want to cancel your notification when user sees that notification, there is a flag for that, FLAG_AUTO_CANCEL:
Notification notification1 = new Notification(R.drawable.icon, "test",
System.currentTimeMillis());
notification1.flags |= Notification.FLAG_AUTO_CANCEL;

How to reset notificationmanager number on notification clear

What's the best way to clear the notification number when the user clicks on the notification? I say the best way, but really I haven't found ANY way. I'm launching a built in activity when the user clicks on the notification, not something I wrote so I can't clear it that way. I've got the notification manager's flag set to clear
NotificationManager notification
.
.
.
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.number++;
nm.notify(1,notification);
But whatever I do the Notification.number keeps going up and never resets to 0.
You could use an intermediary activity which is part of your app and thus can reset your variable and then start the internal activity.
So the chain would be
Notification --starts--> Intermediary Activity --starts--> Built-in activity
I am using a combination of what #CommonsWare recommends and extending an application object as here How to update notification number .
Edit: Further testing shows that actually this is not working because the BroadcastReceiver is called on every notification and because it reset the number on every notification the number is never correct.

Categories

Resources