My app requires people to log in with Facebook. Once they have done so, the Facebook token is checked each time they open the app so that we do not ask them to sign in again - we redirect them straight to the MainActivity.
Note that this is an 'empty view' activity - I have not used setContentView to set it to a view, it is purely there for decision making.
public class DecisionActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FacebookSdk.sdkInitialize(getApplicationContext());
if (AccessToken.getCurrentAccessToken() != null) {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
} else {
Intent startIntent = new Intent(this, SignUpActivity.class);
startActivity(startIntent);
finish();
};
}
}
This is my MainActivity code. Notice that I call my network operations in onCreate because I do not want to call them each time I minimize my app and maximize my app when the activity onResumes. It must be called once when I create my activity.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//show my progress bar
//CALL MY API TO GET SOME DATA FROM THE SERVER
//hide my progress bar after data is received.
}
}
This works perfectly. If the user has signed in, he is redirected to my MainActivity everytime. If the user hasn't signed in, he goes to SignUpActivity.
However, there is one nasty side effect from this code that I discovered today. Scenario 1 on onResuming my app works the way I want it to work.
Scenario 2 on onResuming my app does not work the way I want it to work.
Scenario 1:
If you are in your MainActivity and you minimize your app and press the square button, find your app and maximize again, the MainActivity onCreate method will not be called as the activity simply onResumes therefore no network operations is performed, progress bar is not shown, which is what I want.
Scenario 2:
However, if you minimize your app and decide to click on the app icon on your phone, DecisionActivity will be launched which will decide that MainActivity needs to be launch as the user has logged in already and the token exists. Because MainActivity is relaunched, onCreate is called and network activities are performed and the progress bar is shown, which is not what I want.
How do I stop this from happening when I click on my app icon on my phone?
I checked popular apps like Facebook now to see if they have the same issue by testing Scenario 1 and Scenario 2 on them and they don't seem to encounter this problem which makes me think whether the setup I have used to check whether someone has logged into my app under DecisionActivity can be done in a better way.
I'm sure a more elegant way exists, but this is what I have off the top of my head:
Try using SharedPreferences. So, when your app is minimized, the onPause() method is called. In this method, set the SharedPreference to false, which means that you don't wanna run the progress bar right now. Check for that SharedPreference in your MainActivity's onCreate() method. When the app is resumed, set the SharedPreference to true.
So this means that whenever the user went through the onPause() method, the progress bar won't run either if he goes through the Scenario 1 (because then he will hit onResume(), which won't show the progress bar) or if he goes through Scenario 2 (because your SharedPreference is false, and you check for its value beforehand in MainActivity's onCreate()).
But, now you also have to use the onFinish() or the onDestroy() method, and change the value of your SharedPreference to true, which will make the progress bar to appear when the app is launched next time.
The only flaw I can think of is that I'm not sure whether the onDestroy() method would be called if the user closes the app from the Recents Menu, or if Android mamory cleaner closes the app to free up memory, so do try it and tell me if it works.
And I agree this is but more of a hack and not a proper solution, but if it works, it is good enough ;)
Related
I have an application with basically a list of items and a detail screen for each items.
When initially started, we show the list of items. If the user switches to another app when viewing a details screen, when he comes back to the app, the details screen is shown.
All that is the standard, and working well. However, my client needs the user to come back to the list screen instead of the details screen each time the app is resumed.
My first idea would be to remember the time at which the details activity got paused, and when started, if the time is greater than X seconds, finish and launch list activity instead of resuming.
Any more reliable way to do that?
PS: I know we should not do that, I already explained that to my client, decision is not mine.
Use SharedPreference to save the time of your paused detail activity in onPause and when it resume check the saved time with current time whether it has passed your threshold if it is passed then close it otherwise remain it opened.
Implement this solution and it definitely helps.
Basically, the app is not actually restarting completely, but your launch Activity is being started and added to the top of the Activity stack when the app is being resumed by the launcher. You can confirm this is the case by clicking the back button when you resume the app and are shown the launch Activity. You should then be brought to the Activity that you expected to be shown when you resumed the app.
The workaround I chose to implement to resolve this issue is to check for the Intent.CATEGORY_LAUNCHER category and Intent.ACTION_MAIN action in the intent that starts the initial Activity. If those two flags are present and the Activity is not at the root of the task (meaning the app was already running), then I call finish() on the initial Activity. That exact solution may not work for you, but something similar should.
Here is what I do in onCreate() of the initial/launch Activity:
if (!isTaskRoot()
&& getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
&& getIntent().getAction() != null
&& getIntent().getAction().equals(Intent.ACTION_MAIN)) {
finish();
return;
}
for more details on isTaskRoot()method reference.
You have to provide the following onPause() method to all the activity classes except your list_item activity(Initial Activity).
#Override
protected void onPause() {
super.onPause();
Intent i = new Intent(getApplicationContext(), list_item_activity.class);
startActivity(i);
}
I might understand your problem incorrectly but why you do all the timing stuff? I mean, assuming you've fragments for your list and detail views, just put a a flag to monitor your activity has stopped and listen to catch your activity resume ( via onResume or onWindowFocusChanged ). If it's stopped and resumed then transition to list fragment if it's not already visible.
You can you a broadcast receiver here.
And on activity OnResume method use a call to broadcast receiver and perform whatever you need like this.
#Override
protected void onResume() {
super.onResume();
sendBroadcast(new Intent("YourActionHere"));
}
I have a quiet big application, and sometimes the user uses the HOME BUTTON to 'exit' the application or he receives a call etc. but when he clicks again on the icon, the application is resumed.
What I want is everytime somthing like this happens, the app restarts on the login activity (security process) before resuming the previous activity running before he exit the app.
When the HOME button is pushed, I believe your activity's onStop() and/or onPause() functions will be called. Override one of these methods and set a member variable to check if your activity was interrupted. Now override onResume() to check that variable to determine if you want to start your login activity.
Hopefully this idea gets you in the right direction.
You might also consider creating a super class that extends Activity and override the onStop()/onPause()/onResume() methods to exhibit this functionality. That way, all of your activities outside of your login activity can extend this class, allowing you to place the functionality that you desire in exactly one location.
Pass a Extra to any activity you call after login: intent.putExtra("isLogin", "Yes");
In every activity you call:
declare a field boolean isLogin;
In onCreate:
Intent sender = getIntent();
isLogin = sender.getStringExtra("isLogin","No") == "Yes";
In the onResume of every activity do this:
if(isLogin){
isLogin = false;
}else{
callActivityLogin();
}
Firstly, I'll explain the situation I'm in with my applicaton before I get onto the main question I have. Firstly, I want to find out if the way that I am handling activities is a proper way of handling activities, as I feel it might well not be which in turn is causing my problems.
Currently I have 3 activities setup. Main.class, Login.class and Display.class. The Main activity doesn't have any UI associated with it at all, it launches other activities. So, the first activity that get's launched when the app is launched is the Main activity, and it goes and reads from a SharedPreferences store whether or not the user is logged in. If they aren't logged in, it will open up Login.class, if they are logged in, it will open up Display.class (both of which have UI's associated with them). It uses startActivityForResult().
Because of the way I decided to go around working with activities, I needed to override the back button on both Login.class and Display.class: otherwise, it will go back to the Main.class, and re-run the launch activity sequence, which will inturn re-launch the activity that was just running (therefore the back button is useless). So, I overrode the back button to send back an integer of -1 and then finish() the current activity. I've overridden the onActivityResult(int, int, Intent) for Main.class, and if it receives a -1, it will finish() as well, instead of trying to re-launch the activity.
Now, that all worked for how I wanted it, though I have a feeling that it's the completely wrong way to handle activities, and it's causing problems for me later down the track.
In my application, I decided I wanted to put an app widget and a notification in, both of which will open the app when clicked (via an intent that launches Main.class). I got it all working so that it would do that, though it seems as though starting a new Activity causes problems.
What happens is basically, if the application is already running, and someone pulls down the notification and clicks on it, it will open up a new activity of the same application. This is problematic, especially with the way I handle back buttons: When you open a new one, and press the back button, it will close the new one (as expected,) but will then take you back to the OLD one. You can then press the back button again and exit it, but obviously I don't want to release an app that needs the back button to be pressed twice. What is the best way to sort this problem out? I'm happy to completely change the way I handle activities if that's a major part of the problem.
Thanks,
Josh.
Actually how you set a -1 (which is fine) and then call finish() is perfectly acceptable. I forget the property values, but if you want only a single instance of an Activity there is a way to set that in the manifest ... Task affinity or one of those values - you'll have to look it up.
As an alternate, you can override the Application class and use your own to manage application state. Think of it as a singleton tracker for the main Activity .. "if it already exists use that one, otherwise create a new instance" kind of thing. When creating your main Activity set a reference in your newly extended Application class (make sure you null this out when main is shutting down), then check to see if a reference is available when onCreate() fires again ... if there is a reference already there use that instance of Main .. if not, proceed as normal and set it.
Food for thought ...
I have a feeling that I don't fully understand the depth of the problem, so please forgive me if I'm saying something that you already tried.
But couldn't you remove Main and launch Login from Display? I mean, makes more sense. That's what I do here all the time when I need accessory activities that must fill data for the main activity (which is obviously Display in your case). And when you return from login, you could do all the checks you need. You could allow, say, a "read-only" Display, you could provide a dialog warning...
You would save a lot of trouble and useless code indeed (most of the result/intent spaghetti).
A feasible approach is to have a class extending Application and a couple of Activitys bound to it. A private boolean logged value can determine whether the Display needs to call LogIn to foreground.
public class MyApp extends Application {
private static boolean logged = false;
private Activity logInActivity;
private Activity displayActivity;
public void onCreate () {
super.onCreate();
}
public void setDisplayActivity (Activity act) {
displayActivity = act;
}
public void setLogInActivity (Activity act) {
logInActivity = act;
}
public void finishActivities (Activity act) {
activity.finish();
}
public void setLogged (boolean logged) {
this.logged = logged;
}
public boolean isLogged () {
return logged;
}
public Activity getLoginActivity() {
return logInActivity;
}
public Activity getDisplayActivity() {
return displayActivity;
}
}
class Display extend Activity {
private MyApp app;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (MyApp) getApplicationContext();
if (!app.isLogged()) {
// start LogInActivity. After log in, it will call app.setLogged (true);
}
else {
// continue with Display;
}
}
}
I need to finish all the activities running in an Android application when an exit button is clicked. How can I do that?
What I want:
An option menu on screen with an EXIT option.
When I click the Exit menu, the application should close.
How to make a button for the user to immediately finish all Activities
When the user wishes to exit all open activities, have them press a button which loads the first Activity (passing in an intent to clear out all the other activities) that runs when your app starts. Then inside the one remaining activity (LoginActivity), place some code in onCreate to have it choose to self destruct.
Details:
Create a button and call it "exit", make a buttonlistener for that button, and put the following code in there. What it does is load the activity, makes it the only remaining activity by clearing all activities underneath it.
Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("EXIT", true);
startActivity(intent);
The above code clears all the activities under LoginActivity. The LoginActivity must be the first activity your app runs. So now we are left with only LoginActivity activated. Now we have to get LoginActivity to check the putExtra parameter, and if it has the value "EXIT" then destroy itself as well.
Put the following code inside the LoginActivity's onCreate and onResume, to check the "EXIT" parameter and finish when it is seen:
if (getIntent().getBooleanExtra("EXIT", false)) {
finish();
}
Why is this so hard, why isn't there just an exitAllActivites() function?
Android OS Developers make this hard because they don't want you to give the user an Exit button. They want the user never to care if their App is running or not. If they want it, they run it, if they don't want it, they stop using it. There are benefits to this model, because then an app can be destroyed to make room for a phone call and heavy map-usage, and then they re-run your app, they pick up right where they left off, rather than having to restart from scratch.
So if you use this code, try to cooperate with the Android Developers vision for Android and make it so that the App can be destroyed/recreated at any point, and the user always picks up where they left off. Requiring a user to 'exit' is a hassle.
use StartActivityForResult function for starting a new Activity and when user presses EXIT button, send a unique value as a resultcode. Check for this resultcode in onActivityForResult func and if you find the same unique code then finish the activity.
I don't understand the people who say that this should not be done. Closing an activity can be done quite easily using the built in method to do so.
There is no place in the official documentation that says this should not be done.
For those who think it should not be done for any purpose, maybe you can't think of a reason to do so. There are plenty of legitimate reasons to do this. And since it is a method built in to the Android code.. Google has also decided that you might need to depending on your use. Not all of us only create consumer applications for Android.
So to accomplish this task..shutting down all of the activities in your stack
public void quitApp(){
this.finishAffinity();
}
I created a kiosk app that was run as the default launcher. So I needed a way to exit the app to get to settings or other apps on the device. So in an admin Activity, I placed a pin number pad.. after the user inputs the correct pin, the app needed to exit to the original launcher. I used above code.
You should not implement an Exit button in an Android application.
Read http://groups.google.com/group/android-developers/browse_thread/thread/1bf0f7a4a9c62edd/a0a4aedf21ae5f76?pli=1
Cheers
How to close all activities in Android:
Why are the Android OS devs telling me not to create an Exit button?
If you give developers a hammer, they will use it, and everything becomes a nail, so don't give them a hammer. This is why there is no one-liner to exit all activities.
Why is an Exit button so bad?
You may be frustrated wondering why killing all activities in Android is so difficult. It's hard to do because you've got to bite the bullet and understand how you created the stack, and what it looks like. In what order should the Activities be destroyed? What if the user gets a phone call and needs to nuke your app ASAP? You NEED to have a plan for unwinding. It can't rely on the Activity Manager to babysit your app. If you want to kill your app, the OS devs are twisting your arm to create a unwinding plan because they don't want your application to have any errors when the Activity Manager nukes it.
This self destruct mechanism I go on to describe cooperates with the Activity Manager and causes a cascade effect that causes all activities to finish themselves no matter how they are organized.
To add this cascading self destruct to your app, do these three things:
If you are newbie to android and have never done this before, I suggest you create a brand new bare bones android project to test this out and get a feel for how it behaves. Only when you get the "aha" moment of understanding why this works, then can you extract the usefulness of the code below to delight the user.
Put this override method inside each one of your activities:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
//onActivityResult is called when a launched activity exits, giving
//you the requestCode, 1234, that you started it with. The resultCode
//it returned, 900, and any additional Intent data from it. The
//resultCode will be RESULT_CANCELED if the activity explicitly
//returned that, didn't return any result, or crashed during its
//operation.
switch(resultCode)
{
case 900: //Make 900 a final global variable if you want.
//900 means CLOSE_ALL_ACTIVITIES to me. It is
//a signal that the self destruct button has
//been initiated and all activities should end.
setResult(900); //setResult sets the resultCode that will be passed
//to onActivityResult in the activity that called
//this activity. this will immediately tell the
//parent to self destruct even before the next
//command, finish(), is run.
finish(); //Now stop this activity.
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
If you have more than 3 activities, consider having each activity extending a "MyAppActivity" with the above code in there. If you catch yourself copy/pasting the above method into 15 different activities, slap yourself once, and take a class on good Object Oriented Programming. Then read this.
( Details about onActivityResult. ), ( Details about setResult. )
Whenever you have the user start a new activity, you must do it exactly this way:
Intent myIntent = new Intent(getBaseContext(), YourNewActivity.class);
startActivityForResult(myIntent, 1234);
//The 2nd parameter of startActivityForResult is what is sent to
//resultCode in the onActivityResult method. The code `1234`
//doesn't do anything here because it is a flag that is
//ignored in onActivityResult.
More information about startActivityForResult.
If you don't use startActivityForResult, then the self destruct unravelling won't work.
When you want to exit your application, initiate self destruct like this:
setResult(900); //900 is the self destruct code.
finish();
Then the activity stack unwinding plan cascades through the entire app.
you should do this on yourself. read this good blogpost: http://blog.radioactiveyak.com/2010/05/when-to-include-exit-button-in-android.html
Another solution to finish all activities works for me:
Somewhere in my Controller class, there is a method to initiate the shutdown:
/**
* Ask for application shutdown.
*
* <p>
* After the call, the system shall wipe the entire task and activities and
* then call {#link #onTaskWiped()} to finish the cleaning.
* </p>
*/
public void attemptShutdown() {
// wipe the task and its activities.
Application app = getApplication();
Intent intent = new Intent(app, ShutdownActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
app.startActivity(intent);
}
The ShutdownActivity is just performing data cleanup before finishing itself like it was told before.
/**
* This activity is started by {#link Controller#attemptShutdown()} to wipe the
* entire task and activities. It calls {#link Controller#onTaskWiped()} in
* {#link #onCreate(Bundle)} and finish immediately after.
*/
public class ShutdownActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// this is where you release the application data.
Controller.controller.onTaskWiped();
finish();
}
}
And finally the Manifest part. I have set the transparent style to avoid the small flash when the activity appears but it does not seem to work well.
<activity android:label="Exiting..."
android:name=".ShutdownActivity"
android:excludeFromRecents="true"
android:theme="#style/Theme.Transparent" />
My application starts with a welcome screen Activity, but that screen has an option to skip that screen altogether in future launches.
What's the proper Android way to do this? Initially, I just automatically detected the skipWelcome preference and switched to the 2nd activity from Welcome. But this had the effect of allowing the user to hit the back button to the welcome screen we promised never to show again.
Right now, in the Welcome activity, I read the preference and call finish() on the current activity:
SharedPreferences preferences = getPreferences(MODE_PRIVATE);
boolean skipWelcome = preferences.getBoolean("skipWelcome", false);
if (skipWelcome) {
this.finish();
}
And then I implement onDestroy to move on to the next Activity:
#Override
public void onDestroy() {
super.onDestroy();
startActivity(new Intent(Welcome.this, StartFoo.class));
}
But this makes for some weird visual transitions. I'm starting to think that I need a base Activity that pops open Welcome only if proper, and then goes to StartFoo.
I can't comment on Mayra's answer or I would (not enough rep), but that's the correct approach.
Hidden in the Android documentation is this important phrase for Activity.startActivityForResult(),
"As a special case, if you call
startActivityForResult() with a
requestCode >= 0 during the initial
onCreate(Bundle
savedInstanceState)/onResume() of your
activity, then your window will not be
displayed until a result is returned
back from the started activity. This
is to avoid visible flickering when
redirecting to another activity."
Another important note is that this call does not block and execution continues, so you need to stop execution of the onCreate by returning
if (skipWelcome) {
// Create intent
// Launch intent with startActivityForResult()
return;
}
The final piece is to call finish immediately in the welcome activity's onActivityResult as Mayra says.
There are a few solutions to this.
Did you try just launching the activity and finishing? I vauguely remember that working, but I could be wrong.
More correctly, in if(skipWelcome) you can start the new activity for result, then when onActivityResult is called, immidiately finish the welcome activity.
Or, you can have your launcher activity not have a view (don't set content), and launch either the welcome activity or StartFoo.