Refreshing the activities that are present in the back stack - android

I have a Enter you details option on the action bar as an action item and I am setting that option visible on all activities by extending the below class.
public abstract class ActionActivity extends SherlockActivity {
protected boolean mIsEnterDetailsScreen = true;
protected userName = "";
SessionManager session;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if(mIsEnterDetailsScreen)
{
menu.add("Enter Your Details")
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
else
{
menu.add(userName)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
//This uses the imported MenuItem from ActionBarSherlock
switch(item.getItemId())
{
case 0:
Intent intent = new Intent(ActionActivity.this,EnterDetails.class);
startActivity(intent);
return true;
}
return false;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
session = new SessionManager(getApplicationContext());
sessionCheck(); // This method's body is separated for the easiness and pasted below.
super.onCreate(savedInstanceState);
getSupportActionBar().setHomeButtonEnabled(true);
}
}
So, by the above code, if the user enters details, I am saving it in preferences and at the same time setting the mIsEnterDetailsScreen = false; so that this option would be invisible and user name taken from his entered details would be visible. This works fine only for the activities that are started later. But not on the previous activities. So, my doubt is how to refresh the activities that are there in the back stack. So that even when he presses the back button, still the username has to be displayed even in the previous activities after he entered details instead of Enter you details option.
Suppose if user clicks the Enter you details action item on the 3 rd activity, the changes reflect in the later activities(4, 5, 6....), but not on 1st activity and 2nd activity. Is there any way to refresh activities so that the changes persist.
This is the sessionCheck() method in ActionActivity
public void sessionCheck()
{
if(session.isLoggedIn())
{
mIsEnterDetailsScreen = false;
HashMap<String, String> userHashMap = session.getUserDetails();
// name
String name = userHashMap.get(SessionManager.KEY_USER_NAME);
userName = name;
}
else{
mIsEnterDetailsScreen = true;
}
}

When the user enters details, save them somewhere persistent, I would suggest using SharedPreferences. Then override onResume in your Activities that will be in the backstack and load the details (from the SharedPreferences for example) there.
Android does not use an onRefresh method.

You should have to implement the override method onRefresh on the first activity.
While you close come back from Details Activity or you close/finish the Second Activity(that is your Enter Your Details Activity), onRefresh method of the First activity is called. At that time you can set the Value from the SharedPreference. So, that way you can get the Values reflacted on your view.
Hope you got my point.
Feel free for any comments.

Related

Android: Data passed with intent disappears when coming back from deeper hierarchy

I have an OverviewActivity that contains a listview. When an item is selected, an intent is created to move to the DetailActivity and I pass an int with it.
This int is assigned to a private variable and is used to query the database.
DetailActivity code:
private int mIssueId;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_issue_detail);
mIssueId = getIntent().getIntExtra(IssueOverviewFragment.INTENT_ISSUE_ID, -1);
...
}
In the DetailActivity I can go to a GraphActivity. But when I press the upButton in the GraphActivity, the application crashes because the variable became -1 in the DetailActivity (and the database can thus not be queried properly).
The hierarchy is:
OverviewActivity -> DetailActivity -> GraphActivity
GraphActivity code:
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_graph );
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
...
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main_detail, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_logout: {
Utility.redirectToLogin(this);
break;
}
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
How do I retain the values of my mIssueId attribute in the DetailActivity?
The Intent intends to pass information between activities. When Activity1 pass control to Activity2 mostly related to the behavior of the activity.
If you have information you need to share across a complex hierarchy or to be available for your entire app, you can choice between Shared Preferences if you need persistence or use a Singleton class if the data only will be needed while the app is running.
This is a sample for a Singleton class keeping information to available to the entire app:
public class AppData {
private static AppData ourInstance = new AppData ();
public int score;
public static AppData getInstance () {
return ourInstance;
}
}
And how to access it:
AppData.getInstance().score = 100;
Hope it helps.
It looks like you are getting as a default value because there was an issue getting the intent in DetailActivity. You could try breaking up your request a little with
mIssueId = getIntent().getExtras().getInt();
But I think the issue is probably with how you are putting the int into the intent.
It should look something like
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra(IssueOverviewFragment.INTENT_ISSUE_ID, mIssueId);
startActivity(intent);

Starting a new activity from ViewPager and going back to the same page

In my app there is a viewpager with 3 fragments in it.
In one of the fragments I'm starting another activity.
I want the user to go back to the same viewpager page he were in before on back/up.
I implemented it successfully using the activity that hosts the viewpager's onPause and onResume methods. It worked but the problem was that onResume was being fired after onCreate, which resulted in the app starting at the same page (instead of a "default", different page I set in the onCreate method).
I then tried to place the onResume code in the launching activity's onOptionsItemSelected method, but that didn't work at all.
Launched activity:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
vpPref = getPreferences(MODE_PRIVATE);
int value = vpPref.getInt("viewPagerPage", -1);
if (value != -1) {
MainActivity.instance.mPager.setCurrentItem(value);
vpPrefEditor = vpPref.edit();
vpPrefEditor.remove("viewPagerPage");
vpPrefEditor.commit();
}
return true;
}
return super.onOptionsItemSelected(item);
}
Main Activity: (hosts the viewpager)
public void onPause() {
super.onPause();
vpPref = getPreferences(MODE_PRIVATE);
vpPrefEditor = vpPref.edit();
vpPrefEditor.putInt("viewPagerPage", mPager.getCurrentItem());
vpPrefEditor.commit();
}
The problem is in the first code. I don't know whether this is a placement problem or getting the wrong instance of MainActivity that is wrong...
What could be the cause to this behavior?
That's a bad way of accessing the ViewPager from MainActivity(I hope MainActivity.instance isn't a static field holding a reference to a MainActivity instance). You should let the MainActivity completely handle the ViewPager's position:
In the onPause() of the MainActivity you would keep the current code. In the onResume() method of the MainActivity you would have:
#Override
public void onResume() {
super.onResume();
vpPref = getPreferences(MODE_PRIVATE);
final int value = vpPref.getInt("viewPagerPage");
final boolean shouldRestore = vp.getboolean("restoreStatusFlag");
// we are again in the MainActivity so also make
// restoreStatusFlag as false in the preferences
if (shouldRestore) {
mPager.setCurrentItem(value);
}
}
The shouldRestore preference would be set from the launched activity in the onCreate. This way if MainActivity is started you'll keep the default page(shouldRestore will be false as the launched activity didn't run yet). If after MainActivity you go to the launched activity shouldRestore will become true(you need to set it in the onCreate) and as you come back the ViewPager will be set to the right page in onResume().
This leaves one scenario where the user could go to the launched activity(the shouldRestore will be true) but never comes back to MainActivity with BACK. For this you could test the category from the Intent that started MainActivity(which I'm assuming is the one mentioned in the manifest as the launcher activity):
Set<String> catg = getIntent().getCategories();
for (String cat : catg) {
if (cat.equals(Intent.CATEGORY_LAUNCHER)) {
// reset the shouldRestore flag to false so we end up with the default page
break;
}
}

Android Intent flags

My problem is in understanding how to correctly use intents. After googling and reading all the documentations and articles on this topic, I still cannot sort out my problem. I have two activities: "Searchable" and "ActivityWordInfo". The "Searchable" activity searches a word in the database, and displays search results or suggestions. After the user cliks one of the search results, "ActivityWordInfo" activity is launched and diplays the word definition. Here is some part of the code:
Searchable:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Get the intent, verify the action and get the query
if( savedInstanceState != null ){
//the application is being reloaded
query = savedInstanceState.getString("searchedWord");
doMySearch(query); //does the search in the database
}else{
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
query = intent.getStringExtra(SearchManager.QUERY);
doMySearch(query);
}
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("searchedWord", query);
//saves the searched word if this activity is killed
}
#Override
public void onClick(View v) { //when one of the search results is clicked
int wordID = (Integer) v.getTag();
Intent intent = new Intent(Searchable.this, ActivityWordInfo.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
Bundle b = new Bundle();
b.putInt("key", wordID);
b.putInt("calling_activity", callingActivityId);
intent.putExtras(b);
startActivity(intent);
}
ActivityWordInfo:
public void onCreate(Bundle savedInstanceState) {
...
Bundle b = getIntent().getExtras();
current_word_id = b.getInt("key", 0);
callingActivityId = b.getInt("calling_activity", 0);
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
switch(callingActivityId){
case 3: //which is the ID of Searchable activity
Intent intent3 = new Intent(ActivityWordInfo.this, Searchable.class);
intent3.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent3);
break;
}
break;
}
When the user is in ActivityWordInfo and navigates up, I expect to go to Searchable activity, which should have saved its instance state (the list of results should be still there). What in reality happens:
-The word typed by the user is assigned to 'query' variable and then the results and suggestions are displayed in "Searchable"
-the user clicks one of the words and "ActivityWordInfo" is created
-then, when the user navigates up, the onSaveInstanceState is called for the "Searchable" activity, then it is destryed and created. The result is an empty layout :(
I cannot understand why "Searchable" is destroyed and then created! This only happens in Android 4.2 and not in lower APIs (in 2.3.3 worked perfectly as I expected). Is there any difference in the activity lifecycle in JellyBean?
Note: I cannot use the parentActivity attribute in the manifest since the ActivityWordInfo is called by multiple parents.
The behavior of "up" navigation changed between 2.3 and 4.0. I suggest you look at the topic "Tasks and Back Stack" at developer.android.com.

Destroy Activity stack

There are some applications in which you can logout at any point within. e.g you login and then your browse around. you use the action bar or the menu button to logout. I can call finish() at that very point but then it will just pop the last activity. Even if i move the user forward to the Home Activity, still the stack remains in memory. Is there any way to destroy the remaining stack?
The easiest way to do this is to clear the stack back to your home or first activity, and pass an identifier saying to exit the app. For example:
public class ActivityOne extends Activity {
public static final String FINISH_THIS = "FINISH_THIS";
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if(intent.hasExtra(FINISH_THIS)) {
finish();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getIntent().hasExtra(FINISH_THIS)) {
finish();
}
}
}
public class ActivityTwo extends Activity {
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.id.logout) {
Intent intent = new Intent(this, ActivityOne.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(ActivityOne.FINISH_THIS, true);
startActivity(intent);
}
return true;
}
}
If ActivityOne is the root of your stack and ActivityTwo is where the user selected the option to logout, starting an intent that clears back to ActivityOne will get rid of the backstack. If you want the app to exit when logging out, you can pass an extra like I did with FINISH_THIS to signal the root/home activity to finish.
Another way to do this would be to call setResult(FINISH_THIS) where FINISH_THIS is an int identifier before calling finish(). Then in all other activities in the stack, you'd override onActivityResult and check the result to see if that activity needs to be finished. If it does, you set the result again and keep passing it down the line.
Using the intent method I outlined in the beginning is the preferred method for clearing the stack as it doesn't rely on daisy chaining results together but both options work well enough.
A quick and dirty way to do it would be onResume verify you are still logged in, if not then finish it. That way if they hit back, it will close each activity as it tries to open them. This would also prevent someone from using the app manager to re-enter your activity when you expect it to be closed.
Another idea would be read Android: Clear the back stack

Activity for a screen calls itself for similar usages - but going back quits app

I have one activity representing a screen that is used in two variations. The only difference is that in one case it's used to handle numbers in the other for colors. This is how it is declared:
> public class MainScreen extends Activity {
/** Called when the activity is first created. */
private Integer activityCode;
private static final int ACTIVITY_NUMBER = 0;
private static final int ACTIVITY_COLOR = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityCode = savedInstanceState != null ? savedInstanceState
.getInt("Task") : null;
if (activityCode == null) {
Bundle extras = getIntent().getExtras();
activityCode = extras != null ? extras.getInt("Task") : null;
}
do stuff depending on which activity is actually chosen
}
And this is how it is called
FROM WITHIN ITSELF
:
> #Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
Intent i;
switch (item.getItemId()) {
case OTHER_PAGE_ID:
i = new Intent(this, MainScreen.class);
if (activityCode == ACTIVITY_NUMBER) {
i.putExtra("Task", ACTIVITY_COLOR);
startActivityForResult(i, ACTIVITY_COLOR);
finish();
} else {
i.putExtra("Task", ACTIVITY_NUMBER);
startActivityForResult(i, ACTIVITY_NUMBER);
finish();
}
return true;
....
}
Is this way of re-using the same class actually OK? I use the same class for very similar screens and want to switch back and forth depending on the user selection.
BUT the class calls itself everytime a different screen is selected between NUMBERS <-> COLOR.
The problem is, that when I go from NUMBERS to COLORS and then press back-arrow the app quits. However, when I go from NUMBERS to another screen and press back, it goes back to NUMBERS again.
Why doesn't going back to where I come from work in the case where the class calls itself? I would assume it just puts each call on the stack and comes back to it.
Isn't this just a mini recursion where NUMBER calls itself as COLOR and when finished appears again?
I hope I could make myself clear. Thanks for your help
That makes my head spin. Why not just use two Activities? If it's for code reuse, just have one base class with all your common code, then extend it for your Colour and Number classes:
public class Base extends Activity {
// common code here
}
public class Colour extends Base {
// colour specific code here
}
public class Number extends Base {
// number specific code here
}

Categories

Resources