Android TaskStackBuilder ugly transition - android

What on earth is wrong with TaskStackBuilder that it uses this ugly transition when starting new activities.:
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(this)
.addParentStack(ActivityB.class)
.addNextIntent(new Intent(this, ActivityB.class));
taskStackBuilder.startActivities();
This is basically standard code that google however if you run this code you will see a super ugly transition when going to ActivityB.
I guess its because its a new Task. But I dont want it to look like this is there anything that I can do ?
Thanks !

After digging inside TaskStackBuilder's implementation, the problem is that it forces adding Intent.FLAG_ACTIVITY_CLEAR_TASK to the 1st intent in the stack, which makes that ugly transition, so use the following to start the stack:
Intent[] intents = TaskStackBuilder.create(this)
.addParentStack(ActivityB.class)
.addNextIntent(new Intent(this, ActivityB.class))
.getIntents();
if (intents.length > 0) {
intents[0].setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// Or any other flags you want, but not the `.._CLEAR_..` one
}
// `this` inside current activity, or you can use App's context
this.startActivities(intents, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
The idea here is to still use the TaskStackBuilder for creating your intents' stack, then remove the weird Intent.FLAG_ACTIVITY_CLEAR_TASK that the TaskStackBuilder adds to the 1st intent, then start the activities manually using any Context you want.

Related

Build a PendingIntent with a back stack

I have three activities (Home, Search, Destination), with which I could describe my UX flow. Home activity is my launcher activity, then its the search activity, which is happening to be the parent of Destination activity. So basically what I am trying to achieve is to have a notification, which starts the Destination activity and then when i press the back button I am supposed to go back to the Search activity and then to the Home, but the problem is that once I hit the back button from the Destination activity, the whole stack goes to the background...
In my manifest file, I`ve defined a parent activity for each of the child activities, like it is described here https://developer.android.com/training/notify-user/navigation
This is how my code looks like, when building the pending intent:
// Create an explicit content Intent that starts the main Activity.
Intent notificationIntent = new Intent(this, DestinationActivity.class);
notificationIntent.putExtra("test", destination);
//Intent testIntent = new Intent(this, SearchActivity.class);
notificationIntent.putExtra(DestinationAdapter.DESTINATION, destination);
// notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
// Construct a task stack.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Push the content Intent onto the stack.
//stackBuilder.addNextIntentWithParentStack(testIntent);
stackBuilder.addNextIntentWithParentStack(notificationIntent);
// Get a PendingIntent containing the entire back stack.
PendingIntent notificationPendingIntent =
stackBuilder.getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT);
Could you please help me a bit? Am I doing something wrong?
your code should work, as described here:
https://developer.android.com/training/notify-user/navigation#java
make sure you define parent activity in manifest where relevant:
<activity
android:name=".DetailActivity"
android:parentActivityName=".MainActivity"

Terminate all other previous activities when splash activity starts

In my app I always want user to start from Splash screen. For example, my app may be open in background and some notification pops up which starts splash activity. This should terminate all previous activities which were running.
I have accomplished this by storing list of all running activities references. And when splash activity starts it just calls
for(runningActivity : runningActivitiesList) {
runningActivity.finish();
}
This solution works well. However, Android Studio gives me warning of memory leaks when storing references to activities.
Can someone please suggest me a better approach which avoids memory leaks?
Maybe enough is to start Activity with clear stack:
Intent intent = new Intent(context, clazz);
intent.setFlags(IntentCompat.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
Tried all other options, but only thing worked for me is:
final Intent intent = new Intent(applicationContext, SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| IntentCompat.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NEW_TASK);
return IntentCompat.makeRestartActivityTask(intent.getComponent());
Please NOTE: This solution is also not full proof. Since, when I open my app through Google Play Store it launches splash activity even when another instance of app is running in background. Thus I end up having 2 instances of the same activity.
You don't need to finish all running/previous applications.Instead you can start your activity using TaskBuilder api to handle proper back navigation.
Open your activity with this:
private static PendingIntent makePendingIntent(#NonNull Context context, #NonNull Intent resultIntent) {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Adds the back stack
stackBuilder.addParentStack(YourActivity.class);
// Adds the Intent to the top of the stack
stackBuilder.addNextIntent(resultIntent);
// Gets a PendingIntent containing the entire back stack
return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
}
In your manifest file define the parent activity of YourActivity.class as:
<activity
android:name=".YourActivity"
android:parentActivityName=".MainActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan">
</activity>
Follow these urls for more details: http://developer.android.com/reference/android/support/v4/app/TaskStackBuilder.html http://developer.android.com/guide/components/tasks-and-back-stack.html http://www.programcreek.com/java-api-examples/index.php?api=android.app.TaskStackBuilder
In android manifest set:
android:launchMode="singleTop"
For notifications generated from your app you can use #mac229's flags in #Nischal's pending intent.

Intent.FLAG_ACTIVITY_CLEAR_TOP destroys the destination activity. How to avoid?

I'm trying to return back to an activity which is in the activity stack, deleting all the activitys between the current one and the destination activity.
I Readed that this is the way to achieve it:
Intent i = new Intent(SettingsActivity.this, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
Supposedly it will finish all the activities between the current and the destination, but also is destroying the destination activity.
This is not the behaviour I was looking for. I need to avoid the destruction of the destination activity. It should resume itself instead of being destroyed and recreated.
How can that be achieved?
You need to add FLAG_ACTIVITY_SINGLE_TOP like this:
Intent i = new Intent(SettingsActivity.this, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(i);
When you use FLAG_ACTIVITY_CLEAR_TOP, Android removes all activities on top of the target Activity including the existing instance of the target Activity and then creates a new instance of the target Activity. If you want to use the existing instance of the target Activity, you need to also specify FLAG_ACTIVITY_SINGLE_TOP.

Why does FLAG_ACTIVITY_CLEAR_TOP not work?

As the title says, Why intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) or intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) won't work?
I have 3 Activities let us say A, B and C.
When I am trying to launch Activity A from C with code:
Intent i = new Intent(this, A.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
It simply starts Activity A but doesn't clear the top.! -_-
I have also tried using setFlags().
I read different questions on SO about this problem, but I couldn't find the right answer. >_<
Somebody please help!
Edit
Code for onBackPressed() in activity 'A' as requested by #codeMagic.
#Override
public void onBackPressed(){
if(wvLogin.canGoBack())
wvLogin.goBack();
else
super.onBackPressed();
}
From the documentation for FLAG_ACTIVITY_CLEAR_TOP:
If set, and the activity being launched is already running in the
current task, then instead of launching a new instance of that
activity, all of the other activities on top of it will be closed and
this Intent will be delivered to the (now on top) old activity as a
new Intent.
As you added in your comment, the activity A has been finished before calling B, so this situation doesn't apply. A new instance of activity A will be launched instead.
As I see it, you have two options here:
1) Use the Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK flags. This will start activity A as the root of the stack. It works, but any other activities in the stack will be lost. Assuming A was the first activity (or at least, that you are not interested in any previous activities in the task stack) then it doesn't matter. Note: CLEAR_TASK requires API Level 11.
2) Another possible solution (in case the previous assumption is not true) would be to not use intent flags at all:
B starts C with startActivityForResult().
Instead of calling A, C finishes, having set a result for B indicating that A must be launched.
In B.afterActivityResult(), finish B and launch A.
You are missing the Intent.FLAG_ACTIVITY_SINGLE_TOP flag
Try this:
Intent i = new Intent(this, A.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(i);
You used a diferrent intent: use the one you initialized:
Intent i = new Intent(this, A.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); \\WRONG;;
startActivity(i);
solution:
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); \\RIGHT;;
You could either put a noHistory true to the Activity A in the manifest
android:noHistory=true

Clear the entire history stack and start a new activity on Android

Is it possible to start an activity on the stack, clearing the entire history before it?
The situation
I have an activity stack that either goes A->B->C or B->C (screen A selects the users token, but many users only have a single token).
In screen C the user may take an action which makes screen B invalid, so the application wants to take them to screen A, regardless of whether it is already in the stack. Screen A should then be the only item on the stack in my application.
Notes
There are many other similar questions, but I haven't found anything that answers this exact question. I tried calling getParent().finish() - this always results in a null pointer exception. FLAG_ACTIVITY_CLEAR_TOP only works if the activity is already on the stack.
In API level 11 a new Intent Flag was added just for this: Intent.FLAG_ACTIVITY_CLEAR_TASK
Just to clarify, use this:
Java
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
Kotlin
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
Unfortunately for API lvl <= 10, I haven't yet found a clean solution to this.
The "DontHackAndroidLikeThis" solution is indeed pure hackery. You should not do that. :)
Edit:
As per #Ben Pearson's comment, for API <=10 now one can use IntentCompat class for the same. One can use IntentCompat.FLAG_ACTIVITY_CLEAR_TASK flag to clear task. So you can support pre API level 11 as well.
Case 1:Only two activity A and B:
Here Activity flow is A->B .On clicking backbutton from B we need to close the application then while starting Activity B from A just call finish() this will prevent android from storing Activity A in to the Backstack.eg for activity A is Loding/Splash screen of application.
Intent newIntent = new Intent(A.this, B.class);
startActivity(newIntent);
finish();
Case 2:More than two activitiy:
If there is a flow like A->B->C->D->B and on clicking back button in Activity B while coming from Activity D.In that case we should use.
Intent newIntent = new Intent(D.this,B.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent);
Here Activity B will be started from the backstack rather than a new instance because of Intent.FLAG_ACTIVITY_CLEAR_TOP and Intent.FLAG_ACTIVITY_NEW_TASK clears the stack and makes it the top one.So when we press back button the whole application will be terminated.
With Android's Newer Version >= API 16 use finishAffinity()
approach is suitable for >= API 16.
Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
Its is same as starting new Activity, and clear all stack.
OR Restart to MainActivity/FirstActivity.
I spent a few hours on this too ... and agree that FLAG_ACTIVITY_CLEAR_TOP sounds like what you'd want: clear the entire stack, except for the activity being launched, so the Back button exits the application. Yet as Mike Repass mentioned, FLAG_ACTIVITY_CLEAR_TOP only works when the activity you're launching is already in the stack; when the activity's not there, the flag doesn't do anything.
What to do? Put the activity being launching in the stack with FLAG_ACTIVITY_NEW_TASK, which makes that activity the start of a new task on the history stack. Then add the FLAG_ACTIVITY_CLEAR_TOP flag.
Now, when FLAG_ACTIVITY_CLEAR_TOP goes to find the new activity in the stack, it'll be there and be pulled up before everything else is cleared.
Here's my logout function; the View parameter is the button to which the function's attached.
public void onLogoutClick(final View view) {
Intent i = new Intent(this, Splash.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
finish();
}
Immediately after you start a new activity, using startActivity, make sure you call finish() so that the current activity is not stacked behind the new one.
You shouldn't change the stack. Android back button should work as in a web browser.
I can think of a way to do it, but it's quite a hack.
Make your Activities singleTask by adding it to the AndroidManifest
Example:
<activity android:name=".activities.A"
android:label="#string/A_title"
android:launchMode="singleTask"/>
<activity android:name=".activities.B"
android:label="#string/B_title"
android:launchMode="singleTask"/>
Extend Application which will hold the logic of where to go.
Example:
public class DontHackAndroidLikeThis extends Application {
private Stack<Activity> classes = new Stack<Activity>();
public Activity getBackActivity() {
return classes.pop();
}
public void addBackActivity(Activity activity) {
classes.push(activity);
}
}
From A to B:
DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(A.class);
startActivity(this, B.class);
From B to C:
DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(B.class);
startActivity(this, C.class);
In C:
If ( shouldNotGoBackToB() ) {
DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.pop();
}
and handle the back button to pop() from the stack.
Once again, you shouldn't do this :)
Advanced Reuseable Kotlin:
You can set the flag directly using setter method. In Kotlin or is the replacement for the Java bitwise or |.
intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
If you plan to use this regularly, create an Intent extension function
fun Intent.clearStack() {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
You can then directly call this function before starting the intent
intent.clearStack()
If you need the option to add additional flags in other situations, add an optional param to the extension function.
fun Intent.clearStack(additionalFlags: Int = 0) {
flags = additionalFlags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
Try below code,
Intent intent = new Intent(ManageProfileActivity.this, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|
Intent.FLAG_ACTIVITY_CLEAR_TASK|
Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Try this:
Intent logout_intent = new Intent(DashboardActivity.this, LoginActivity.class);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(logout_intent);
finish();
For me none of the above methods not work.
Just do this to clear all previous activity:
finishAffinity() // if you are in fragment use activity.finishAffinity()
Intent intent = new Intent(this, DestActivity.class); // with all flags you want
startActivity(intent)
Intent i = new Intent(MainPoliticalLogin.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
In Java: -
startActivity(new Intent(getApplicationContext(),ChooseServiceActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
Sometimes your android emulator might fails to connect eclipse DDMS tool and ask for adb to start manually. In that case you can start or stop the adb using the command prompt.
I found too simple hack just do this add new element in AndroidManifest as:-
<activity android:name=".activityName"
android:label="#string/app_name"
android:noHistory="true"/>
the android:noHistory will clear your unwanted activity from Stack.

Categories

Resources