Reputation: 8944
The application(target API level must be 7th) has FragmentActivity
which analyzes at onCreate
the fragment key passed as an extra.
Now what is needed is to reorder to front the activity that is already created with the given fragment key.
Let's say the FragmentActivity
with different fragment keys are FA1
, FA2
and FA3
- each is the same activity Class instance with different fragments.
Now in the stack FA1
> FA2
> FA3
i want to use the intent rather than the back button to get to FA2
, by default that gives:
FA1
> FA2
> FA3
> new FA2
.
I'd like to get either FA1
> FA3
> FA2
as the FA3
might have some pending operations, FA1
> FA2
is not as good but definitely better than default.
If there were several activities I'd use the FLAG_ACTIVITY_REORDER_TO_FRONT
flag for intents, but that does not work for this case.
FA1, FA2, FA3, etc.
are all the instances of the same class MyFA
, that's why I'm not able to use the intent flag and the FragmentManager seems to be out of help until there's a standard global fragments cache.
Milestone (currently working and to be improved) solution One thing I've learned today is activity-alias which allowed to make several aliases for the same activity with the different Intent extras used as id's. Now with the REORDER_TO_FRONT flag it works as I wanted.
Solution feedback The solution has no low-level operations, I like a lot more than digging at the tasks or back-stacks. Now the drawback is that each of such activities needs a separate alias with the hardcoded path, I don't really like it.
Requirements (bounty is here) Whoever comes with a decent optimization takes 150 300 cookies. Not bad ? Any other solid solution is also highly appreciated.
Currently I have like 10 aliases at application manifest, e.g.
<activity
android:name=".activity.FragmentActivity"
android:configChanges="orientation"
android:screenOrientation="portrait" >
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.company.name.intent.FragmentActivity" />
</intent-filter>
</activity>
<activity-alias
android:name="com.company.name.intent.FragmentActivity.FragmentedOne"
android:targetActivity=".activity.FragmentActivity" >
<intent-filter>
<action android:name="com.company.name.intent.FragmentActivity.FragmentedOne" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="fragment_key_extra"
android:value="FragmentOne" />
</activity-alias>
<activity-alias
android:name="com.company.name.intent.FragmentActivity.FragmentedTwo"
android:targetActivity=".activity.FragmentActivity" >
<intent-filter>
<action android:name="com.company.name.intent.FragmentActivity.FragmentedTwo" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="fragment_key_extra"
android:value="FragmentTwo" />
</activity-alias>
And then the activities are reordered with
Intent intent = new Intent(
"com.company.name.intent.FragmentActivity.FragmentedOne");
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
Upvotes: 3
Views: 1925
Reputation: 14059
Based on your idea of using activity-alias
to solve this issue, I wrote a Historian class that will do the following:
startActivity()
and activityDestroyed()
methods that will do some bookkeeping so the lookup table can be used to dynamically assign an alias to a running Activity based on the Intent.Here's an example on how to use it:
in AndroidManifest.xml
<activity android:name=".MyFragmentActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias android:name=".Alias0" android:targetActivity=".MyFragmentActivity" />
<activity-alias android:name=".Alias1" android:targetActivity=".MyFragmentActivity" />
<activity-alias android:name=".Alias2" android:targetActivity=".MyFragmentActivity" />
<activity-alias android:name=".Alias3" android:targetActivity=".MyFragmentActivity" />
<activity-alias android:name=".Alias4" android:targetActivity=".MyFragmentActivity" />
within your Activity class
public class MyFragmentActivity extends FragmentActivity
implements Historian.Host {
private Historian<MyFragmentActivity> mHistorian;
// ...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHistorian = new Historian<MyFragmentActivity>(this);
// ...
}
@Override
protected void onDestroy() {
super.onDestroy();
mHistorian.activityDestroyed(this);
}
@Override
public boolean matchIntent(Intent intent0, Intent intent1) {
if (intent0 == null || intent1 == null) return false;
final String title0 = intent0.getStringExtra("title");
final String title1 = intent1.getStringExtra("title");
return title0.equals(title1);
}
// ...
}
Instead of starting a new instance of your activity like this:
Intent intent = new Intent(this, MyFragmentActivity.class);
intent.putExtra("title", newActivityTitle);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
You do this:
Intent intent = new Intent(this, MyFragmentActivity.class);
intent.putExtra("title", newActivityTitle);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
mHistorian.startActivity(this, intent);
So you still need to add a few activity-alias
into your manifest manually (to be used as a pool) and implement the matchIntent()
method in your Activity (to help detect whether two Intents are equal to you) but the rest is handled dynamically by the Historian class.
I haven't tested the code exhaustively, but it seems to work fine on some simple tests that I did. The idea is actually very similar to my answer on the other question (just need to use FLAG_ACTIVITY_CLEAR_TOP
instead of FLAG_ACTIVITY_REORDER_TO_FRONT
there) but using the activity-alias
instead of the inner child classes make it much cleaner :)
Upvotes: 1
Reputation: 1628
This answer is worth 300 cookies :-D Mmmmm
Extend Application so we can create our own back stack list with a global scope.
import android.app.Application; import android.content.Intent;
class MyApp extends Application {
//This is our very own back stack.
private ArrayList<Intent> backStack = new ArrayList<Intent>();
public void reorderBackStack(){
//Do what you want to the order of your backstack
//You can read the value of your intent extras from here.
}
public void addIntent(Intent i){ // this gets called from our activities onCreate
backStack.add(i);
}
public void goBack(){
if(backStack.size() >= 2){ // can go back
backStack.remove(backStack.size() - 1); // drop the last item in stack if you want. This is how Android does it. (no forward capability)
this.startActivity(backStack.get(backStack.size() - 1));
}
}
}
Add a reference to our custom back stack in our activity.
public class MainActivity extends Activity { MyApp myapp;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myapp = ((MyApp)getApplicationContext());
myapp.addIntent(this.getIntent()); //add the intent for this instance to backStack.
myapp.reorderBackStack(); // call this anytime we need to reorder the back stack.
}
@Override
public void onNewIntent(Intent newIntent){
this.setIntent(newIntent);
myapp.addIntent(this.getIntent());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
@Override
// We won't be using the back stack we will be using our own list of intents as a back stack so we need to override the back button.
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
myapp.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
Override the back button so we can use our own back stack instead of Android's.
Edit
I put together a proof of concept.
The .apk is Here on my google drive.
The complete project folder is here.
Zipped downloadable project folder is here.
Please don't complain about my code. I know I used strings where I should have used constants and I duplicated code instead of seperating it and my spacing isn't perfect and I used excessive loops and objects. I haven't sorted out the savedInstanceState. Again, this is just a proof of concept. It works and I thought it might help someone.
Upvotes: 4
Reputation: 45942
I have answered something similar, but the solution is not the best as FA1 > FA2 > FA3 will take you to FA1 > FA2 rather than FA1 > FA3 > FA2.
My answer was for question : How to tag Activity
Anyway, I edited the code a bit to use a "tag" which will be a string, but I think you can use the main solutions that uses integer indices. In summary, this should allow you to fallBackToActivity
with a certain TAG. I am not sure how will each Activity decide what will its tag be, but again, in my previous answer it was a simple matter of an incremental integer.
package sherif.android.stack.overflow;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class SuperActivity extends FragmentActivity {
private static String EXTRA_INDEX = "SUPER_INDEX";
private static int RESULT_FALLBACK = 0x123456;
private String tag; //this is your tag
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getIntent()!=null) {
tag = getIntent().getStringExtra(EXTRA_INDEX, "default_tag");
}
}
protected final String getIndex() {
return tag;
}
protected final void fallBackToActivity(String tag) {
Intent intent = new Intent();
intent.putExtra(EXTRA_INDEX, tag);
setResult(RESULT_FALLBACK, intent);
finish();
}
//@Override
//public void startActivityForResult(Intent intent, int requestCode) {
// intent.putExtra(EXTRA_INDEX, getIndex());
// super.startActivityForResult(intent, requestCode);
//}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_FALLBACK) {
if(!data.getStringExtra(EXTRA_INDEX, "default_tag").equals(getIndex())) {
setResult(RESULT_FALLBACK, data);
finish();
}
}
}
}
Upvotes: 1
Reputation: 24031
Here is the solution:
1. In your FragmentActivity
implement a Interface
for the callback from Fragment to Activity.
2. When you start any Fragment put it in back stack with the argument or tag (addToBackStack(String arg0))
with the help of which you can pop up with that tag.
3. When you need to reorder the fragments then call the method of the interface which you implemented with proper arguments as per requirement and then use popBackStack(String arg1, String arg2)
to get the fragment with the tag you put it in the backstack.
Upvotes: 2
Reputation: 6385
You can look into using the functionality provided by the FragmentManager. It has functions such as popBackStack
.
You can also use FragmentTransaction to add fragments to the backstack. You can use addToBackStack
to accomplish this.
So with these two classes you should be able to reorder your fragments in the backstack as necessary.
Upvotes: 2