Just a bit about my app first. Its a quiz app. It has a main screen displaying a question, which loads straight away, and arrows going previous and next which go to other quiz questions. I'm using the same layout over and over, just by passing different question data, so when I click the "next" button, it will just launch an intent to the same class, just with different data. At the start of my one single layout, I have a little check to see if the user has clicked to hide the disclaimer or not.
So my disclaimer pops up at the start of the app. It has a dismiss button and a Dont show this again checkbox. I can get the checkbox working perfect, using SharedPreferences but the problem arises when they hit the Dismiss button. Since I'm reusing the same layout, any time the user navigates to a new question, the disclaimer pops up. I only want it to popup on the first screen, e.g. when the app loads.
I have tried setting another SharedPreference to hide the disclaimer when the user hits dismiss but once I hide it, it never comes back, because when the user loads the app back up again, that shared preference is still set. My problem is knowing where to set the preference back! I tried resetting it in onPause() but that didn't work.
So, what I'm asking is, how can I determine if an activity is the first one to be loaded so I can only do the Disclaimer check then, and not in each of the subsequent screens?
Thanks.
EDIT: Here's my OnCreate() method:
final static String disclaimerShownPref = "disclaimerShown";
final static String disclaimerShownOnce = "disclaimerShownThisSession";
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
//Checks to see if the boolean is set
//The second argument is the default to use if the preference can't be found
if(!mPrefs.getBoolean(disclaimerShownPref, false))
{
if(!mPrefs.getBoolean(disclaimerShownOnce, true))
{
new Disclaimer(this);
}
}
topMostLayout=buildHomeScreen();
setContentView(topMostLayout);
}
In your case I would suggest using a static variable in your activity:
private static boolean sFirstTime = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if( sFirstTime )
{
/*
* Place the disclaimer check here
*/
sFirstTime = false;
}
else
{
}
Having said that. I would consider a better approach to switch between questions in the same instance of your activity instead of creating a new one every time.
Good luck!
Instead of restarting the activity each time the user navigates between questions, you should just write a function that will repopulate the views with the new question data when previous or next are clicked. As for the disclaimer, if you want it to show up every time the app is started, then setting the variable using shared preferences is not the best way to go about it. The shared preferences are not deleted when the app closes, they are only deleted if you erase them manually or uninstall the application altogether. Instead create a variable in your activity that tracks whether or not "Dismiss" was clicked. As long as the app is running the dialog will not be displayed, but when the app is restarted all the variables including the one that keeps track of displaying the dialog will be reset and it will show up again.
Instead of using an Intent to launch a new version of your activity, why not just use an onClickListener to call setText() and change the display question? This might make the Activity lifecycle more intuitive and probably simplify your code.
Related
I intend to show four welcome screens to the user that only appear once for new users. To do so, I save a flag in the Preferences at start up and check its value to determine if the user is new or not. If not, then the welcome screens do not appear:
SharedPreferences mPrefs;
final String welcomePref = "oldUser";
#Override
public void onCreate(Bundle savedInstanceState) {
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
Boolean welcome = mPrefs.getBoolean(welcomePref, false);
if (!welcome) {
Intent intent = new Intent(this, welcomeScreenOne.class);
startActivity(intent); //start the first welcome screen
SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean(welcomePref, true); //not a new user anymore
editor.commit();
}
}
The welcomeScreenOne activity starts the second welcome screen and so on.
As you may have noticed, the error in this code is that if the user views the first welcome screen, the pref is set to true and so if he exits the application before looking at the other welcome screen (2, 3 and 4) then returning to the app will not display the remaining screens.
To solve this I thought of using startActivityForResult(Intent, int) inside each welcome screen activity so that the 4th returns to the 3rd, which returns to the 2nd which returns to the 1st welcome screen, then setting the pref to true. Is this bad coding practice?
My second solution is calling the 1st screen from the main, returning then calling the 2nd, returning then calling the 3rd and so on.
Maybe there is a way I do not know of, please advise?
Is this bad coding practice?
IMHO, yes.
Maybe there is a way I do not know of, please advise?
Have one activity, not four.
Use something else inside this one activity for your sequence of welcome screens, such as:
Four fragments, showing one at a time
Four views, showing one at a time
An existing library for such welcome screens, such as these wizards or these "showcase views"
It's possible for you to add a splashActivity to your app? if so, you can check on that activity if the user is new or not, and if is new, show the welcome screen and if not, send it to your mainActivity (or the activity you need).
It depends on the activity mode you are launching the activity if you will use launch mode Single Instance than the same activity will get reopened again and again ,if the default mode will be there every time a new instance will be created and there will be activities piled up in the stack which can create unknown results
I am calling a Master/Detail activity in android by clicking on a button placed in another activity (UserActivity). The strange thing is, that if i click the back button in the Master/Detail activity, I loose the Data in the state of the UserActivity. It wants to execute the onCreate Method again.
If I click on the login-button in the LoginActivity where i am redirected to the UserActivitiy and i go back with the Back-Button of the "Smartphone", the username and password i typed are still there. So there i do not loose the data.
Is there a difference between the back-button of the Smartphone and the back-button at the top of the program? I am a bit confused now and i know how to persist the state of the Activity. But my question is, why i am having this behavior on the one side and on the other not.
Just in case you will leave the question like this and don't add code:
What will probably help is checking for the amount of items that are already there. When I ran into this issue, the onStart() got called so quickly that it seemed to me that the Activity has lost the data. Actually it DID have the data, but calling onCreate/onStart (I'm in a Fragment) NULLed it.
What I did to avoid this is to check if there is a need to load items in the list. If there is, it calls a method that contains what the old onCreate/onStart did. If there is no need to load data, it will just skip the step and live with the "old" data happily for the rest of its life.
#Override
public void onStart() {
super.onStart();
if(DummyContent.ITEMS.size()<3){
initializeApp();
}
}
void initializeApp(){
videoTitles=new ArrayList<String>();
videoUrls=new ArrayList<String>();
. . .
}
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 ;)
I am developing an application in which i am overriding the back button. I created a check box. On click of which i am calling intent for:
startActivityforResult();
And also maintaining the state of activity as :
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean("checkbox", checkbox.isChecked());
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
checkbox_state = savedInstanceState.getBoolean("checkbox");
}
which run fine and the state is maintained.
Means i am entering value in edit text. and on check box click calling new activity for result and while return on first activity the state is maintain.
Now but from second activity if i click on device back button the state is not maintained.
So what should i do to maintain the state on back button. I searched but not found satisfied solution. Please suggest me.
When You start the new Activity then the current Activity gets hidden and new Activity is placed on top of the stack. Now if you press the back button on the new Activity then the first activity should come up in its current state (If it wasn't destroyed, calling finish()) where it was left i.e. if the check box was checked then it should remain checked. You don't need to save the activity state unless the orientation is changed or the activity is destroyed.
Are you sure you are not doing any thing in the onActivityResult or onResume() method which effects the state of the check box? I would recommend to first comment out all the code in both the methods and see if your check box retains the state. Also can you also make sure that the code itself doesn't uncheck the checkbox before starting the new Activity?
Now but from second activity if i click on device back button the state is not maintained.
onSaveInstanceState() is mostly used for configuration changes (e.g., rotating the screen).
So what should i do to maintain the state on back button
Most likely, there is no "state" that needs to be "maintained", outside of your data model. Your activity needs to update its data model: files, database, preferences, ContentProvider, some singleton that is your in-memory data model manager, whatever.
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;
}
}
}