I have an app that runs a service (audio player) in the background and displays an ongoing notification while it is run. When the user clicks the notification, I'd like to open the player activity without it showing up in the recents list and without bringing the rest of the app to front, so when the user presses the back button they go directly to the app they were using previously, regardless of everything. I tried three different ways and none of them worked well.
Start an intent with FLAG_ACTIVITY_NEW_TASK. If there is an existing task, it's brought to the front. When the user presses the back button, the player activity finishes and reveals the previous activity in the said task instead of going back to the app that had been in foreground the moment the notification was clicked.
Start an intent with the following flags: FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. The existing task isn't brought to the front and the back button works as expected. However, after this activity finishes, the app disappears from the recents list completely. The only way to get it back is to start it from the launcher icon which brings that existing task to the foreground as if opened from recents.
Start an intent with the following flags: FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_ACTIVITY_NO_HISTORY. The activity starts in a new task, but said task replaces the main one in the recents list. Adding FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS makes it behave exactly as in previous case.
Is there any proper way to do such navigation, or should I give up trying and just make it bring an entire existing task to the front with the player activity launched on top of it, as in case 1?
Is it convenience for u to declare the launchMode to "singleInstance" inside this Activity Tag in the Manifest File? I think that may help u.but i am not sure.U can try
Maybe you can try launchmode as a tag for your notificationActivity in your manifest;
android:launchMode="singleInstance"
you should try new document-centric API:
Adding Tasks to the Recents Screen
I actually figured out the proper way to do this: add the taskAffinity attribute to your <activity> in the manifest. This way, it won't interfere at all with how the rest of the app displays in recents, exactly what I was trying to achieve.
Related
I'm trying to understand the Android Tasks and Back stack by reading the official documentation: https://developer.android.com/guide/components/activities/tasks-and-back-stack
I have a couple of questions regarding the documentation.
Question 1: In Figure 4. in the documentation, there are 2 tasks - a foreground and a background task, the background tasks contains Activity Y and X, if the Activity Y is declared with a singleTask launch mode, how is it possible to create a task with activity Y on top of activity X?
For other questions, I prepared a simple project - 2 activities, A and B. On both activities I have 2 buttons:
Button A opens Activity A
Button B opens Activity B
Activity A is the MAIN (LAUNCHER) activity.
Question 2: The documentation says that using the intent flag FLAG_ACTIVITY_NEW_TASK produces the same behavior as using the singleTask launcher mode:
This produces the same behavior as the "singleTask" launchMode value, discussed in the previous section.
This is not what I see from my test application. If the activity B has a launcher mode set to singleTask and if my backstack is A -> B -> A -> A, then if I open B the back stack will look like A -> B (it will pop the last 2 As). I guess that the reason behind it is the tasks created: when the launcher intent was sent it opened the activity A in the new task (let's call it to task 1), when I opened B, it opened it in task 2 (because it has a singleTask launch mode), then the 2 A activities where opened in Task 2 as well. After opening B again, Android found a task that already has a B activity (Task 2) and it brought it to the front, poping 2 As.
However, using a FLAG_ACTIVITY_NEW_TASK instead of singleTask does not produce the same behavior, it just opens B on top of everything else. Is the documentation wrong, or am I doing something wrong?
Question 3: The example at the end says:
the two launch modes that mark activities as always initiating a task, "singleTask" and "singleInstance", should be used only when the activity has an ACTION_MAIN and a CATEGORY_LAUNCHER filter. Imagine, for example, what could happen if the filter is missing: An intent launches a "singleTask" activity, initiating a new task, and the user spends some time working in that task. The user then presses the Home button. The task is now sent to the background and is not visible. Now the user has no way to return to the task, because it is not represented in the app launcher.
How to reproduce that? In my previous example, I open the activity B in a new task, but if I hit the home button and the launcher icon again, I get back to activity B. So even though I'm using a singleTask launcher mode, I'm still able to return to that task by using the launcher icon or selecting it from the list of the recent applications.
It's entirely possible that I'm doing something wrong here and that new tasks are not generated, is there a way to see all tasks and activities for a specific application?
Thanks.
Today I spent more time trying to understand what's going on here and I think I have the answers. I realized that nothing in my example created new tasks. Tasks are visible in the Recent screens (apps in the background) - at least on Android 5+.
So, the first question that should be answered is: Why creating activity B is not creating a new task?
It's because generating new tasks goes hand in hand with the taskAffinity property. If you don't specify this property, your activity will have the default taskAffinity which is your application's package name. When you open the activity with a singleTask launcher mode, Android will look for tasks with the same affinity (task's affinity is defined by its root activity affinity) and if it finds one it will add your activity to that task. Because I didn't specify the affinity, android assumed I want to add my activity to the task with an affinity equal to the application's package and it didn't create new tasks at all. It just added an activity to an existing task.
That being said, FLAG_ACTIVITY_NEW_TASK and singleTask are very much different, although sometimes they produce the same behavior. If you trigger an intent with the FLAG_ACTIVITY_NEW_TASK flag that is trying to open an activity A without specifying activity's taskAffinity you'll just normally add an activity on top of the current task. (This is still confusing to me, I would expect it to open an activity only if the activity is not in the stack; if it is, it should do nothing.)
Opening the same activity without this flag but using the singleTask mode, will again not create a new task but will:
Add the activity on top if it's not in the stack (this is the answer to question 1)
Destroy all activities on top of yours and call onNewIntent if the activity is already in the stack. (I think that branch.io uses this method to handle deep links - which seems really hacky to me, but...)
On the other hand, if you specify the taskAffinity for your activity then:
if singleTask mode is set, android will search for the task with this affinity and if it's present it will
add your activity on top if the activity is not on task's backstack
destroy all activities on top of your activity and call onNewIntent if the activity is in the back stack.
If the task is not present, it will create a new one and add your activity as root.
if FLAG_ACTIVITY_NEW_TASK flag is set in the intent, android will search for the task with the specified affinity and if it finds one it will just bring that task to the foreground, without destroying the stack or calling onNewIntent. (I tested that if the activity is in the back stack, I guess that if the activity is not in the back stack it would push it to the task's stack).
So, to answer question 2. The flag FLAG_ACTIVITY_NEW_TASK and the launcher mode singleTask are different. I guess that similar behavior can be achieved by using 2 more flags FLAG_ACTIVITY_CLEAR_TOP and FLAG_ACTIVITY_SINGLE_TOP.
I think that the example in the documentation regarding question 3 is relevant for older versions of Android (lower than 5.0). There different tasks were not shown in the Recent Screens - here's the documentation: https://developer.android.com/guide/components/activities/recents.
I need to launch an activity when a notification is clicked.
The Activity launched is a standalone activity and can not be opened from any other flow from the app itself. I need to ensure that no one can navigate back to this activity once it is destroyed.
Currently i am using the following configuration:
<activity android:name=".Activities.SingleRestaurantOfferActivity"
android:taskAffinity=".Activities.SingleRestaurantOfferActivity"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
android:resizeableActivity="false"/>
And I am launching this activity with the intents
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK
Now when the acitivity is created (first notification click), it creates a new task and as per my understanding, if another notification is clicked, since the task will be existing, it will clear the task and re launch the new activity with the new intent. Is my understanding correct?
If the app is open. Then the user clicks on the notification and launches the activity. Now the home button is pressed. What will happen to the notification activity? The recents screen will only show the actual app task and not the notification activity task, will the notification activity be destroyed eventually or will it leak memory?
Please guide me as to how i should approach this. The official android guide also uses launchMode:singleTask, do i need to use that as well?
I am answering my own question here so that anyone who faces a similar issue gets an idea as to what they can do.
Steps to follow are:
1) In the manifest of the required activity, add the following:
android:taskAffinity = "com.yourpackage.youractivity"
This ensures that this activity has a seperate task affinity as compared to the default affinity. This will come into picture when excludeFromRecents is added.
android:excludeFromRecents = "true"
This flag tells android that the task associated with the given activity should not be shown in recents screen. If we had not added the taskAffinity explicitly, this would have meant that the whole application would not show in the recents screen which would be irritating. adding the task affinity will only hide the task of the required activity from recents
android:noHistory = "true"
I am not sure if this is necessarily needed. I added it to ensure that as soon as the user navigates away from the activity in any way ... eg home button, it will kill the activity using onDestroy. Also exclude from recents will prevent it from showing in the recents screen.
2) Now comes the part of intent flags to launch the activity:
I have used the flags:
FLAG_ACTIVITY_NEW_TASK :
This flag along with the taskAffinity will create a new task for the activity if the task is not already created with the same affinity. Then it will place the activity at the root of this new task. If the task already exists, it will be brought to front and the new intent will be delivered to the activity in onNewIntent() method. I wanted the activity to get fully recreated so i added the other flag below.
FLAG_ACTIVITY_CLEAR_TASK:
This flag clears the task and kills all the activities in it. Then it adds the new intended activity at the root of the task. This will ensure in my case that the activity gets fully destroyed and recreated from scratch. You might not need this in your case. CLEAR_TOP with standard launch mode will also almost do the same thing but that's a whole different scenario.
Hope this helps people creating standalone activities which don't merge directly into app flows.
I have made a system overlay Icon similar to the one here: https://github.com/mollyIV/ChatHeads
I want it to open my app using the click listener of the icon. I am able to make it open MainActivity using context.startActivity(intent) with FLAG_ACTIVITY_NEW_TASK.
But I would like to open my app as if it was opened from the task switcher(Whatever activity is open, in the same state). How do I accomplish this?
You are using FLAG_ACTIVITY_NEW_TASK
If set, this activity will become the start of a new task on this
history stack.
Try to use FLAG_ACTIVITY_SINGLE_TOP:
If set, the activity will not be launched if it is already running at
the top of the history stack.
Source: http://developer.android.com/reference/android/content/Intent.html
I'm encountering the following scenario in my application:
Installing it and immediately opening it (using the open button
after the installation, instead of the icon in the applications
list).
Navigating through a few activities.
Clicking the home button.
Clicking the application icon to load it again.
Instead of returning to the activity I was in, the initial activity
is loaded. The previous activity is still there and can be accessed by clicking the back button, but there isn't really a way to know that and it looks like the app was completely restarted.
This may be an issue in this app, because in some cases it might require a registration after performing a few actions, and losing the focus on the activity could be very annoying
Is there anything that can be done to avoid that?
There's a couple of flags under Intent that might be helpful. You use them when an Activity is launched:
"FLAG_ACTIVITY_TASK_ON_HOME: If set in an Intent passed to Context.startActivity(), this flag will cause a newly launching task to be placed on top of the current home activity task (if there is one).
FLAG_ACTIVITY_REORDER_TO_FRONT: If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought to the front of its task's history stack if it is already running."
http://developer.android.com/reference/android/content/Intent.html
I have a Notification which starts an Activity B with FLAG_ACTIVITY_NEW_TASK. The documentation says:
[...] if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front [...]
In the case that the user taps the Notification while in Home Screen or a different app, this works fine. But if the user is in an Activity of my app, let's say in Activity "A", and then taps the Notification multiple times, Activity "B" is started multiple times. This leads to Back key not bringing back to Activity "A".
What am I doing wrong here? And where's the difference between being in home screen or a different app and being in Activity A of my app?
Regards
Now I found out that I had to set a different affinity in the manifest to start the Activity in its own task. That solved my problem, too.
You probably want FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP for the notification activity. That will make sure that there is only one instance of Activity B, and it is re-used .
The difference is that when your Activity A is in the foreground you already have task running , otherwise (most probably) not. Read this for more info about tasks.