i want to make a notification like BBM connected notification (for android)...
so when i open my app, the notification will appear, and it cant be canceled...
so i use this code
nm=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification=new Notification(android.R.drawable.stat_notify_more, "this is important", System.currentTimeMillis());
notification.flags = notification.FLAG_NO_CLEAR; //notification never dissapear
Context context=MainActivity.this;
CharSequence title="hello there";
CharSequence detail="this is demo";
Intent intent=new Intent(context,MainActivity.class);
PendingIntent pending=PendingIntent.getActivity(context, 0, intent, 0);
notification.setLatestEventInfo(context, title, detail, pending);
nm.notify(0, notification);
i put that code in public void onCreate(Bundle savedInstanceState)...
so, the notification cant cancel when i click or slide.
and when i close my app, the notification still there.
then i have an idea to use this code :
#Override
protected void onDestroy() {
nm.cancelAll();
}
but, this function never called???
how can i cancel the notification when i close the app?
It might be because onDestroy will not be called immediately when you navigate away with the home button.
Android has callback methods available that work well with notifications etc.
These are called onUserLeaveHint() and onUserInteraction().
The JavaDoc for onUserLeaveHint() states:
Called as part of the activity lifecycle when an activity is about to
go into the background as the result of user choice. For example, when
the user presses the Home key, onUserLeaveHint() will be called, but
when an incoming phone call causes the in-call Activity to be
automatically brought to the foreground, onUserLeaveHint() will not be
called on the activity being interrupted. In cases when it is invoked,
this method is called right before the activity's onPause() callback.
This callback and onUserInteraction() are intended to help activities
manage status bar notifications intelligently; specifically, for
helping activities determine the proper time to cancel a notfication.
I'd suppose you will want to override either one of these; especially onUserLeaveHint() seems a good choice for your purpose.
#Override
protected void onUserLeaveHint()
{
nm.cancelAll();
super.onUserLeaveHint();
}
You can't intercept the event of 'force closing' the application process. Android doesn't support this. When the user does this, the system calls Process.killProcess(int pid) and all resources will be freed. It's just not possible to catch this event and perform tasks before the process is really killed. Maybe a workaround is possible, but it wouldn't be how Android wants it.
There is no guarantee that the method will called. If application will not have enough memory, it can be killed by OS.
There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.
You missed the super.onDestroy
#Override
protected void onDestroy() {
nm.cancelAll();
super.onDestroy();
}
Related
I have a simple application in which before I do anything, I must check whether the user is logged in or not.
To do this, I inherited the Application class like this:
public class GBApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
if (userIsLoggedIn()) {
Intent overviewActivity = new Intent(this, Overview.class);
overviewActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(overviewActivity);
}
}
}
userIsLoggedIn() returns a boolean stored in the SharedPreferences file.
Everything works, but when I press back from the Overview activity, it redirects me to the default activity (MainActivity), and removing it from the AndroidManifest.xml files gives error.
Also, I can't call finish() since this is a non activity class.
How can I terminate my program after I return from the Overview class? I found solutions where they pass a value to the MainActivity, call finish() from there. But this seems to complicated.
What else I can do?
This is my application tag in AndroidManifest.xml
Your plan is going to cause problems. Every time your process is created, you are starting an activity, even if an activity is not needed for this particular process.
Displaying a launcher activity — such as in response to the user tapping on a home screen launcher icon — is one way that a process might be created for your app. But there are many others:
AlarmManager
JobScheduler
a Notification
a push message (e.g., from Firebase Cloud Messaging)
a request sent to a ContentProvider that you export from your app
a request sent to a Service that you export from your app
a manifest-registered BroadcastReceiver
the user returning to your task in the overview screen
and so on
In none of those scenarios is displaying this activity necessarily appropriate. Yet, your code will display the activity in all of them, because any time Android forks a process for your app, you display this activity.
Have all of your activities (other than the login one) see if the user is logged in, and route the user to the login activity if that is needed. When the user completes the login, and the login activity finishes, the user is returned to where they were trying to go, whether that is your launcher activity or some other activity (e.g., they returned to your task after your app had been in the background for a while, so your process was terminated, but Android tries to send them back to whatever activity of yours they had been in last).
You could simply override the onBackPressed() of your overviewActivity and pilot the direction your app goes when the back button is pressed (which in your case is to shut down the app):
#Override
public void onBackPressed(){
startActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
}
The code above just shuts down your app immediately the back button is clicked once (which is pretty boring.. but simple). You could also try the code snippet below; it pops up a dialogue box:
#Override
public void onBackPressed(){
new AlertDialog.Builder(this)
.setMessage("Are you sure you want to exit?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int id){
startActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
}
})
.setNegativeButton("No", null)
.show();
}
I have created a class which extends Service and runs as a foreground service. I would like my service notification to be persistent (i.e. not removed by swiping). However, my notification can be dismissed by swiping.
The Service documentation states: ...A foreground service must provide a notification for the status bar, which is placed under the Ongoing heading. This means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground...
I did put break points to check whether onDestroy() or stopSelf() is hit, but this is not the case. The service is running in foreground mode, but I can dismiss the notification by swiping.
I have found quite a few questions regarding the opposite case, where it was not possible to dismiss the notification after the service was stopped, but did not find any question similar to my problem.
The service is started via an Intent and initialized as follows:
#Override
public void onCreate()
{
super.onCreate();
initialize();
}
private void initialize()
{
Notification n = get_service_notification();
startForeground(10, n);
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter(BroadcastCodes.service_broadcast_intent_name));
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
startServing();
return START_STICKY;
}
where startServing() subscribes to location updates
The notification is built like this
private Notification get_service_notification()
{
Intent intent = new Intent(this, LoginActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
b.setAutoCancel(false)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.app_icon)
.setTicker("some text")
.setContentTitle("some text")
.setContentText("some text")
.setContentIntent(contentIntent)
.setContentInfo("")
.setOngoing(true);
Notification res = b.build();
res.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
return res;
}
I have tried playing around with the flags and setOngoing/setAutoCancel, but the result stayed the same.
SDK versions are configured as minSdkVersion 18, targetSdkVersion 25
I would really appreciate any input regarding this issue - it might be just some stupid mistake, but I already did spend more than a few hours trying to research on my own...unfortunately to no avail.
EDIT 1
I still have not figured out the problem. I decided to try a dirty hack, but still no luck.
I made a PendingIntent and registered it via setDeleteIntent() the idea being to display a new notification once this one was dismissed by swipe. Unfortunately, I was not able to get this to work (the intent never fires when swiping).
I would also like to clarify, that the notification CAN BE swiped away, but IS NOT AFFECTED (i.e. is not deleted) by the clear-all button in the notification pane (the trash icon).
EDIT 2
As mentioned above I was not able to use setDeleteIntent() to recreate my notification when it is dismissed by swipe.
For now I settled with a workaround - my service does some periodical tasks. I am now calling
notificationManager.notify(10, service_notification);
when my task runs so that even if swiped away, my notification will be recreated after a while.
I still have a strong feeling that I just misread the documentation, since there are several notifications on my test device (Honor API23) that can not be swiped away.
EDIT 3
I have tried the same code on a different device (Lenovo API23) and the notification works as expected (can not be dismissed and can not be swiped). My problem seems to be device specific.
I made a few further tests (besides the ones mentioned in updates). I tried running various examples/tutorials from around the internet like this page here
On the Honor I was initially running my tests the notification could always be swiped, on the Lenovo and others the "ongoing" flag works as stated in the documentation, i.e. it can not be dismissed via swipe action.
To sum it up, my problem really seems to be device specific.
My activity starts a service which runs a CountDownTimer. The timer sends broadcasts back to the activity as it counts down. The activity processes the broadcasts in the onReceive method of a BroadcastReceiver. All of this works fine.
My problem comes when the following events happen in this order:
App is stopped (via onPause())
Timer finishes
App is resumed (via onResume())
When the app is resumed the service is no longer sending broadcasts, so the activity does not know how much time is left on the timer or if it's finished. This prevents the activity from updating the UI.
I've tried a dozen ways of dealing with this, and read through many Stack Overflow questions and answers, but I've yet to find a solution. I would think that there's a way to pick up a broadcast that was sent while the activity was not active, but I've yet to find a way.
For the record, here is my relevant Activity and Service code:
activity.java
// Start service
timerIntent.putExtra("totalLength", totalLength);
this.startService(timerIntent);
// ...
// BroadcastReceiver
private BroadcastReceiver br = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getExtras() != null && inSession) {
session.setRemaining(intent.getExtras().getLong("millisUntilFinished"));
updateProgress();
}
}
};
// ...
// onResume
#Override
public void onResume() {
super.onResume();
registerReceiver(br, new IntentFilter(TimerService.COUNTDOWN_TS));
}
service.java
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
long length = intent.getExtras().getLong("totalLength");
countDownTimer = new CountDownTimer(length, 1000) {
#Override
public void onTick(long millisUntilFinished) {
timerServiceIntent.putExtra("millisUntilFinished", millisUntilFinished);
sendBroadcast(timerServiceIntent);
}
#Override
public void onFinish() {
}
};
countDownTimer.start();
return super.onStartCommand(intent, flags, startId);
}
What's the best way to process the broadcasts that the service sent while the activity was stopped?
Use the BroadcastReceiver to store the last request (SharedPreferences perhaps) it received and check it when the Activity starts.
Alternatively, instead of processing a countdown using broadcasts, just store the time that the countdown would end. The Activity can then handle the countdown all by itself as it knows when it should end. Using a service and broadcasts seem to be a little over-engineered for such a simple task.
Update:
From the way you have described your task, I see you needing to handle 2 scenarios. This is how I would likely do it.
Assuming that "XYZ" is the service\intent\whatever starting the countdown and "ABC" is the Activity displaying the progress. (ABC and XYZ could be the same activity if that is what you wanted)
Requirements:
When the countdown starts, I would make XYZ store the time that the countdown should end in SharedPreferences.
ABC is already running when the countdown starts. As Commonsware said, the Eventbus model is excellent for handling this scenario so long as XYZ and ABC are running in the same process. Just fire an event to read the preference value and count down to the specified time. If the user closes ABC and reopens it, Scenario 2 will kick in.
ABC is not running. Check in OnResume whether the countdown time has elapsed. If not, set up ABC to display the countdown again. If there is no countdown active, do something else.
If you also need to do something when the countdown has elapsed regardless of whether you have a UI active, then again Commonsware's suggestion of AlarmManager is perfect.
Let's pretend for a moment that using a Service with a CountDownTimer to track some passage of time for the purposes of updating an Activity actually is a good idea. It's not out of the question, assuming that the Service is actually doing something for real and this timing thing is some by-product.
An activity does not receive broadcasts while stopped, mostly for performance/battery reasons. Instead, the activity needs to pull in the current status when it starts, then use events (e.g., your current broadcasts) to be informed of changes in the data while it is started.
This would be simplified by using something like greenrobot's EventBus and their sticky events, as the activity would automatically get the last event when it subscribes to get events. Using greenrobot's EventBus for this purpose would also reduce the security and performance issues that you are introducing by your use of system broadcasts to talk between two Java classes in the same process.
Also, please stick with lifecycle pairs. onResume() is not the counterpart to onStop(). onStart() is the counterpart to onStop(); onResume() is the counterpart to onPause(). Initializing something in one pair (e.g., onResume()) and cleaning it up in the other pair (e.g., onStop()) runs the risk of double-initialization or double-cleanup errors.
What's the best way to process the broadcasts that the service sent
while the activity was stopped?
Using sticky broadcast intents from the service and then retrieving them from the activity would be a way to process the broadcasts that the service sent while the activity was stopped. I can only offer that as a possible solution rather than claiming it is the "best way".
http://developer.android.com/reference/android/content/Context.html#sendStickyBroadcast(android.content.Intent)
They have however, been deprecated since API level 21 due to security concerns.
Instead of using Normal broadcast you can use Ordered broadcast (sent with Context.sendOrderedBroadcast). For this along with defining a BroadcastReceiver in your activity you required to define BroadcastReceiver in your manifest with same intentfilter. Only change is while registering BroadcastReceiver in your activity you need to set priority to high, so that when your activity is running and activity's BroadcastReceiver is registered it gets called first, and inside onReceive of this BroadcastReceiver you can use abortBroadcast for getting the BroadcastReceiver called which is defined in your android manifest. Now when your activity is not running the BroadcastReceiver defined in your android manifest will get called. So this way you can have the status and if you wish you can display updates to user by notification even if your activity is not running.
I've got a couple of activities and an intent service which handles GCM incoming messages.
Right now for every push, I'm sending a Notification, and after the user clicks it, he is redirected to appropriate screen.
I would like to alter this behavior that if the app is visible (any activity is in the foreground), instead of the notification a dialog message is shown (with appropriate action).
Any idea how to implement it?
I have 2 ideas but none of them is perfect:
Keep track of every activity in the application, if the activity is visible, don't show notification, but sent an intent to the activity (not nice solution)
register/unregister the second broadcast receiver in each activity's onResume/onPause, "catch" the incoming GCM broadcast (I'm not sure if it is possible).
Any other solutions?
A possible solution (idea 1):
To detect whether your app is running back- or foreground, you can simply set a boolean in onPause/onResume:
#Override
protected void onResume() {
super.onResume();
runningOnBackground = false;
}
#Override
protected void onPause() {
super.onPause();
runningOnBackground = true;
}
When you start a new intent from an notification this method gets called: (if you are using singleTop), with the boolean you can determine what to do in the onNewIntent method.
#Override
protected void onNewIntent (Intent intent){
if(runningOnBackground){
//do this
}
else{
//do that
}
}
Hope it helps!
I didn't test it, but the docs say you can get the number of running activities per each task.
Try to find your application's task among currently running tasks:
ActivityManager acitivityManager = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
// Get the top of running tasks, limit by 100
List<RunningTaskInfo> tasks = acitivityManager.getRunningTasks(100);
for (RunningTaskInfo taskInfo : tasks) {
if (YOUR_PACKAGE_NAME.equals(taskInfo.baseActivity.getPackageName())) {
if (taskInfo.numRunning > 0) {
// Show dialog
} else {
// Show notification
}
break;
}
}
Google added a note on getRunningTasks():
Note: this method is only intended for debugging and presenting task management user interfaces. This should never be used for core logic in an application, such as deciding between different behaviors based on the information found here. Such uses are not supported, and will likely break in the future. For example, if multiple applications can be actively running at the same time, assumptions made about the meaning of the data here for purposes of control flow will be incorrect.
So use it at your own risk.
Also check if GCM broadcasts are ordered. If so, you can "override" your default BroadcastReceiver with the other ones in each Activity. Just play with the priority of IntentFilters. When the BroadcastReceiver with higher priority receives the message, it can abort it's further propagation. For your application this means that when some Activity is running, it registers the receiver which shows the dialog and aborts broadcast. If no activity is active, then your default receiver shows the notification.
I have an activity, which I used as a custom loading dialog (the dialog is pretty complex and has custom UI, which fits the customer colors schemes, etc.).
The loading dialog is created and shown from class, which is not derived from Activity class (this I am mentioning becuase the startActivityForResult() cannot be called):
private void showLoadingDialog()
{
Intent intent = new Intent( this, ActivityDialogLoading.class );
intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
startActivity( intent );
isLoadingDialogShown = true;
}
The dialog is shown when long running asynchronous tasks are performed by the application on background.
Now when the background tasks if finished I want to close the loading dialog (The loading dialog has also the Cancel button, so client can interrupt the task, but this is other story).
I did not found a way to get the reference to the instance of the running ActivityDialogLoading class to call the finish() method...
The way I am using is a bit complicated - I override BroadcastReceiver so it takes the reference to the ActivityDialogLoading in its constructor. In OnResume() method of the ActivityDialogLoading I register as a receiver for broadcasted intent.
When I need to close the loading dialog i broadcast the intent... Is there a better way?
Saying the same with other words: What is the best way to close an activity when the close action is caused by some background event in application engine?