This question already has answers here:
How to exit from the application and show the home screen?
(22 answers)
Closed 5 months ago.
So, I'm trying to create an app which have a function like an alarm clock for Android phones, it will alert the user at their chosen time. When the alert screen show up, the user has an option to push a notification or snooze, and the app will push a notification and then close.
However, when I click the snooze or the notification button, the app did not close. Instead, the app stop the alarm sound and minimized (just as when the user touch the home button). If I touch the recent app, I then can open the alert screen again.
Edit: I know that there're some codes such as System.exit() or killProcess, since those code are not recommended, I prefer avoid using them.
The reason I ask is because I tested the real clock app that come with my phone (4.3), and the it's alert screen will close after the I press the snooze or dismiss button. So there must be a way for me to do the same, right ?
Answer
Okay, so as Sagar Pilkhwal explained below, and after reading others related problems, I found out that there's no "good" method to close you app by codes, you have to leave that option to the users or OS. Unless you want to use System.exit or killProcess, but they're bad ways to force your app to close.
Sagar Pilkhwal also have a alternatively method to this problem, if you don't want your alert screen show up in recent app, you bring up another activity.
However, as for this case and Alarm Manager in general, open MainActivity will lead to nasty stuffs, since MainActivity is when we handle Alarm events. Then I found out another solution, if you don't want your alert screen show up in recent app, simply add
android:excludeFromRecents="true"
to your Alert.class Activity in the Manifest, or add Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTSto the intent used to start Alert.class.
This will lead to other problem, as when the user press the Home button, the alarm won't turn off, and the user can't open recent app to access the alert screen (he'll has to open the app to do so). I fix this problem by try to detect the home button pressed event. Detail answer are below.
well im thinknig.
System.exit(0);
maybe? im new to android thou..but i sometimes use it..
Put the following to Manifest, in your Alert activity :
android:excludeFromRecents="true" //this will make the Activity be exclude from recents list.
You can also add android:launchMode="singleInstance" and android:taskAffinity="" (if you know what they do)
In the Alert class, use this to detect home button pressed event:
#Override
public void onPause() {
if (!isFinishing()) {
createNotf(); //Handle home button press here.
}
super.onPause();
}
Use this to handle back button pressed: (You have to have this code if you want to detect home button press using isFinishing();
#Override
public void onBackPressed() {
//Handle BackButton event here.
}
For my case only, when the user press home button, I will create a notification, so he can either click on the notification or open the app again to access to Alert Screen.:
private void createNotf() {
Intent screenIntent = new Intent(MyAlert.this, MyAlert.class);
screenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
screenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
PendingIntent pIntent = PendingIntent.getActivity(MyAlert.this, MainActivity.SEND_ALARM_CODE, screenIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification.Builder ntfBuilder = new Notification.Builder(this)
.setAutoCancel(true)
.setContentTitle("ALARM_RUNNING")
.setLargeIcon(bitmap)
.setSmallIcon(R.drawable.ic_launcher)
.setContentText("CLICK_TO_OPEN_ALERTSCREEN.")
.setContentIntent(pIntent);
NotificationManager myNotfM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
myNotfM.notify(999, ntfBuilder.build());
}
When the Alert stop, that notification will also be clear:
#Override
public void onDestroy() {
super.onDestroy();
NotificationManager mntfM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mntfM.cancel(999);}
System.exit(0);
That right there will close your application out leaving nothing running in the background.However,use this wisely and don't leave files open, database handles open, etc.These things would normally be cleaned up through the finish() command.
I personally HATE when I choose Exit in an application and it doesn't really exit.
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.
I have a flow in my application like this:
For new users:
Splash Screen --> Login Activity --> Home Activity
For already registered users:
Splash Screen --> Home Activity
Basically the Splash Screen has an if else to decide which activity to go to. Once a first time user logs in, his status is saved in a preference variable for the splash screen to decide next time not to open the login activity.
Now the situation is that. If a new user logs in and goes to the home activity, and then logs out. He is redirected to the Login screen which is pretty much what should happen. But, in case an existing user opens the app, he is shown the Splash screen and directly moved to the Home Activity. Now if the user logs out, he gets out of the app. This happens because the Login Activity does not have any instance created and thus finishing the Home Activity finishes the whole app. Logout actually finishes the Home Activity, naturally the last active activity should open up. Which is not happening.
What I want to do is that, I want to implement a logic which will check that the Login Activity is available or not. If its available then finish() will be called else the Login Activity will be called via intent.
Please tell me how to achieve this.
P.S: My app uses a custom theme with a customized action bar. If I call finish and Intent together or I use flags to clear existing activities then there is a weird transition effect which shows the black standard action bar for a split second thus creating a bad user experience.
Now if the user logs out, he gets out of the app. This happens because
the Login Activity does not have any instance created and thus
finishing the Home Activity finishes the whole app.
If i understood your question, why dont you just call the Login Activity manually after user click a logout button?
Its what i always did with apps that have flow like yours
when user login finish login activity and start home activity.
when user logout finish home activity and start login activity
You always can call Login Activity via intent. If the activity is available, android will show this activity. Else android will create new activity automatically.
Actually that's why we use intents to show activity instead of creating activityes manually. System catches this intents and does all dirty work.
EDIT:
Hmm, but wouldn't you have the transition problem anyways? (If you were already logged in, and then log out - using intent/finish() you will have the same black action bar issue no?)
Maybe consider following ( I actually did this in my app):
Merge Splash screen and Login into one activity and depending whether you are logged in - display the login fields or proceed to home screen. Then you have a out of box consistent stack of activities regardless of use cases and no mambo-jumbo with do I already have this or not.
I can't comment because of I lack 4 rep, so I'll post as answer here:
I think #Blaze Tama is right. You can also use FLAG_ACTIVITY_CLEAR_TOP on intent to avoid stack flow problems:
From docs:
If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.
Always start Login activity and start Home activity right away if the user already logged in.
In the Splash Screen activity
Intent intent = new Intent(this, Login.class);
If (user already logged in)
{
intent.putextra("Logged in", true);
}
startActivity(intent);
In the Login activity
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null)
{
if (intent.getBooleanExtra("Logged in", false))
{
startActivityForResult(new Intent(this, Home.class), requestCode);
}
}
else
{
// The existing code here
}
}
In Home activity send back a code to indicate if the user logged out or just BackPress. If BackPress finish this Login activity.
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();
}
I am making an app that gives an alarm when you are close to a gps position. now when a user presses the home button this app get send to the back. When the users enters the region to trigger the alarm, the alarm triggers but the app isn't send to the foreground.
How can i do this?
I tried this
Intent intent = new Intent(context, TrainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(intent);
in the function of the alarm, but this does nothing? (Trainactivity is the class that runs on the background and the class that needs to be on the foreground)
The REORDER_TO_FRONT flag only controls the stacking order on the activity history. That is not what you want. Is this what you are looking for ? - Bring application to front after user clicks on home button