I'm trying to write an android application that has two main activities: login screen and display screen. Currently I'm setting it up so that if the user is already logged in, the login screen will be skipped.
In order to work out the best way to do this, I created a generic "Main" activity that looks something like this:
public class Main extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
// Get preferences
SharedPreferences settings = getSharedPreferences("PREFERENCES", MODE_PRIVATE);
super.onCreate(savedInstanceState);
//setContentView(R.layout.viewusage);
//System.out.println(settings.getBoolean("LOGGEDIN", false));
if (settings.getBoolean("LOGGEDIN", false) == false)
{
Intent intent = new Intent(Main.this, Login.class);
startActivity(intent);
}
else
{
Intent intent = new Intent(Main.this, Display.class);
startActivity(intent);
}
}
}
The idea is when someone logs in successfully, the login activity will save all the details needed to the user preferences, and then finish() the activity. This will return to the Main activity. What I'm trying to do is have the main activity keep looping through the if ... else statement when there aren't any activities running: so when the login page closes, it will re-run the if ... else statement, and should launch the Display activity rather than the Login activity. The reverse will be true if the user logs out from the Display class.
I've thought of using a while (true) loop, but that just hangs the program. I've thought of putting it in a separate thread, but I don't know how to detect if there is already another activity running, so a separate thread will just keep looping and opening new activities (I presume, I haven't tried it)
Any suggestion on how I could work this out?
Thanks.
You can't do it by looping in onCreate, because the activity doesn't actually get off the ground until onCreate returns.
One possibility is to use startActivityForResult and arrange for the Login and Display activities to return a code indicating whether to quit or proceed to the other activity. Then you would place your logic in onActivityResult instead of looping in onCreate.
Another, perhaps cleaner approach is to design the Display activity to directly start the Login activity when the user logs out, and vice versa. That way, the Main activity can just call finish() after it determines which activity to display initially.
Off the top of my head.
Write a login class that timestamps the login and expires the login after a set time interval perhaps with a method isLoginStillValid() and save the login object on a soft kill in onRetainNonConfigurationState. Retrieve the login object in onCreate. Save the login fields, if applicable to preferences in onDestroy(). Set a flag isSavedInstanceState to true in onRetainNonConfigurationState and use this to determine if you need to write to prefs in onDestroy. I know this sound complicated, but this is a reusable template.
So in the end, in onCreate, look for the login object. Check to see if the login has expired. If so, post a GetLogin dialog. In each credentialed call, check to see if the login has expired before allowing access to the credentialed call. If the login has expired, consider posting the GetLogin dialog again. Remember, time may have passed since the app came to the foreground.
Finally, launch GetLoginDialog using startActivityForResult so that you can retrieve the login data and update the login object with a new timestamp.
JAL
I'm a little confused by your question, but I think you want to use startActivityForResult rather than startActivity. This will allow you to return a true/false result from your Login Activity so that you can then launch your Display Activity if the login succeeded. Similarly for your Display Activity, if the user logs out, you would finish() the Activity and return a "logout" result to Main that logs the user out.
Related
I am working on an app with two activities : LoginActivity and MainActivity. When the user first opens the app he will login and his credentials (username and token) are saved in Preferences.
Now, if the user opens the app again then MainActivity should start. I tried to switch between these activities in Application class and removed intent-filter for LAUNCHER_ACTIVITY from manifest, but it doesn't work.
Is there any way of switching between Launcher Activities programmatically on basis of saved preferences?
Is there any way of switching between Launcher Activities programmatically on basis of saved preferences ?
You can try this:
Step #1: Have LoginActivity have the LAUNCHER <intent-filter> as normal, and have MainActivity have no <intent-filter>.
Step #2: Have an <activity-alias> element in the manifest pointing to MainActivity that has the LAUNCHER <intent-filter>.
Step #3: Put android:enabled="false" on the <activity-alias>, so it is disabled by default, so when the app is first installed, the only launcher icon is for LoginActivity.
Step #4: When the user logs in, and you want to change so MainActivity is the launcher activity, use PackageManager and setComponentEnabledSetting() to make the <activity-alias> enabled and to disable the LoginActivity.
Not all home screens will detect this change on the fly, and for those, the device would need to reboot in all likelihood to pick up the change. For this reason, it would be better to stick with a single launcher activity. If you want, that launcher activity could have Theme.NoDisplay and simply route to the correct "real" activity in onCreate(), per Fahim's answer.
Long story short, you cannot change the Activity that is launched by default. Update: There is an alternative as described by CommonsWare in another answer.
However, there are reasonable work arounds. In your MainActivity you can check whether the user is logged in and immediately redirect them to the LoginActivity. That has the added benefit of automatically returning to the MainActivity after you have logged in.
Alternatively, you can always go first to the LoginActivity, and if the user is already logged in, send them to the MainActivity (rewrite the Intent history to remove the return to LoginActivity or set the noHistory flag in the manifest).
The easiest way is to make MainActivity launcher activity, as usual.
Then check in MainActivity#onCreate(Bundle) via SharedPreferences if the user already logged in and, if not, start LoginActivity immediately. When user logs in, save the boolean flag indicating that user logged in in SharedPreferences and finish MainActivity.
An activity doesn't necessarily require a UI, so you can use the launcher activity in the manifest to lauch any activity you desire.
As far as I know changing launcher programmatically is not possible, but it also doesn't make sense.
On your LoginActivity's onCreate check if a username and token is already saved, if it is try to login with that automatically, is succeed redirect to your MainAcivity. Depending on the way your app works you can have a variable that checks if a user is logged in or not, if he is the LoginActivity would redirect him to MainActivity without trying to log in again.
//LoginActivity
onCreate(Bundle bundle)
{
/* ... */
//Or whatever you use to login (it could also go inside a thread or an AsyncTask
if (login())
{
//Intent
Intent intent = new Intent(this, MainActivity.class);
//Start Activity
startActivity(intent);
//Finish this activity, so when user pressed back the login activity will not come forth and the app will exit
//this looks like when a user has logged in once, the login screen will not be visible to him (unless you want to)
finish();
}
}
You can also configure it to save username and token only if a login is successful which means the above code can be modified like this:
if (getUsername() != null)
{
/* Start Main Activity */
}
This won't attempt to log in, but it knows the credential are right since it has logged in at least once with them.
If your app behaves a different way that these methods do not work, feel free to say so, I may be able to provide more info
You can jsut add Intent after OnCreate to the XML you want to show in the beginning of your APP.
public class LoginActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//add some code to detect if user is logged in
if (user != null){
Intent in = new Intent(LoginActivity.this, YourDesiredActivity.class);
startActivity(in);
}
..........
...........
My app begins with Activity A (log in screen) which I always want on the bottom of the Activity stack.
Based on some logic after login, I launch some other Activity B-Z. There is a menu in the app which allows you to switch around between any Activity B-Z.
If the user hits the back button enough times, I don't want them returned to the login screen. I want the user to be sent to the Android Home screen if the back button is pressed on the Activity which has Activity A as the next Activity on the stack. This Activity is not guaranteed to be the Activity which was launched by Activity A because my Activities use singleTop.
Ideas?
The only other option I can think of is to remove singleTop and whatever Activity is launched by Activity A could remember that (my Activities all derive from a base class, which I would use to do that).
Another possibility may be to do something like the following in the onBackPressed handler:
if (getParent().getClass().getName().equals(ActivityA.class.getName())) {}
Although not a direct answer to your question, but if the problem is that
I don't want them returned to the login screen
then the classic solution is to finish() the login Activity when the user has logged in successfully. This way you'll make sure the user will never return to that Activity.
To do this, Why don't you get the User Login information, and store it in your apps private shared preferences. Then when the User launches your application, you can read the Shared preferences from activity A, and automagically log them in to activity b .
something like this:
SharedPreferences myPrefs = getBaseContext().getSharedPreferences("MyPrefs", SharedPreferences.MODE_PRIVATE);
if((myPrefs.getString("username",null) != null) && (myPrefs.getString("password",null) !=null)){
// Make sure your user is a member of your application
// auto log in to the home page
Intent bIntent = new Intent(getBaseContext(), BIntent.class);
startActivity(bIntent);
}
For more reference on how to use Shared Preferences
http://developer.android.com/reference/android/content/SharedPreferences.html
Then if you want to be really slick, Store the Class name when the onDestroy() method is called in each Activity, overwriting each class as the last open Activity. So whichever activity the user was last on before the application was closed is stored in preferences and you can read that in the login activity, then from there launch b-z
This will make it so that the login activity is always in memory, since it is first launched to check your users credentials.
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();
}
I have a typical Android app where the user has to log in before displaying the main Activity of the application. However, if we've already got a cached username / password, I want to skip the login page.
I could, in my Login onCreate, detect if I've got a user/pass and push a new Activity, but I'm worried that this will make my app startup slower (since I have to load an activity then immediately throw it away), and it also perhaps breaks the back button (i.e. you can hit back and end up back at the login screen).
Is there any way to avoid this and only load the full Login Activity if there is no cached password?
You can make a separate helper activity which launches either your login activity or your main activity. In its onCreate, you'd use startActivity and immediately call finish to remove the helper activity from the back stack.
Intent intent;
if ( /* already logged in */ ) {
intent = new Intent(this, MainActivity.class);
} else {
intent = new Intent(this, LoginActivity.class);
}
startActivity(intent);
finish();
Then, LoginActivity should re-launch MainActivity as normal. This way, the user will never be able to navigate back to the helper activity as it does not appear in the back stack. Do note however that the user can still login, go to the MainActivity, pause that activity, remove his account (through the Android settings) and resume the activity (from the recent apps). If you want to prevent this, you're probably better off placing the login redirect in MainActivity instead (perhaps even in onResume).
You could create a Splash Activity.
On the AndroidManifest.xml's Splash Activity tag, add the android:noHistory="true".
On the Splash Activity, check whatever you need (maybe with an AsyncTask if it may take a long time, to avoid freezing the Activity) and, depending on the result, you start the Login Activity or any other Activity.
Another approach is to transform login screen from Activity to Dialog. That should be easy. And then make your main activity check if there is cached username/password or not. In the second case show LoginDialog to user.
I also have a typical Android app with the same requirement, and I solve it as follows.
First I configure the initial launch activity to be MainActivity, and then inside onCreate(), check if the user has saved credentials (this is all done through AccountManager and authenticator service).
If the check fails and user needs to authenticate, then I start the LoginActivity. If logging in fails or the user presses back, the MainActivity calls finish() on itself to close the whole app. Otherwise, after logging in, the MainActivity is resumed and is presented to the user properly.
The advantage of doing it in this order is that (in my case anyway) the user is more likely to be logged in than out, and so this will avoid any start/stopping of unneeded activities as you say.
A second advantage is that, if the MainActivity (or another activity) is resumed at a later time and the user's session has expired, you can simply start the LoginActivity again to reauthenticate them.
Hope that all makes sense :)
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.