I am trying to do something very simple, but yet it seems that its very complicated in Android
I have to support the following use cases
Launch app from launcher
Launch app from notifications
Assuming I have 3 activities (splash, main, details) and 2 notifications
one tied each to main and details
Activity Notification
Splash
Main NotifMain
Details NotifDetails
which show up in the notification shade I end up with the following scenarios
Launch app from launcher
Splash -> main
App is in foreground showing Main and user taps on NotifMain
I see a second instance of main is launched even though main is configured as singleTask
App is in background and user taps on NotifMain
Works as expected, and there is only one instance of Main
User is on Main, but taps on NotifDetails
This causes NotifDetails to show, now, since in navigation because Details should take user back to Main, if I do that, I see Main twice
User is on Details, and taps on NotifMain
I see Main, on which if I hit back it takes the user back to Details, on which if I hit back takes the user back to Main again.
This is very confusing and complicated. Is there a simple way to handle this scenario..
All i wish to do is.. that irrespective of my paths
if the usr taps on NotifMain, then there is only Main running for the application, with all other tasks cleared.
If the user taps of NotifDetail, then you have Main -> details in the application.
Related
So my scenario is as such.
Let's say there is a MainActivity, which only job is to start, call installSplashScreen().setKeepOnScreenCondition { true } to show the Splash screen using the new backward compatible APIs, and then after checking some state it does startActivity(SomeActivity); finish()
Now we're on the SomeActivity and if we press the home button, the app is gone on the background. Then if we click on the launched icon, the SomeActivity is launched correctly, and the MainActivity's onCreate is never called, therefore the splash screen does not show again, and the SomeActivity shows instantly.
But if instead of pressing the home button, we press the back button, and the app is backgrounded that way, then when we click on the launcher icon, the MainActivity's oncreate is called again, and the splash screen icon flashes for a tiny fraction too making it look jarring.
My question is, does this sound like it's some wrong configuration on my part, or am I stuck with this behavior as long as I am not on a single activity architecture?
You are confused. Pressing the BACK button does not "send the app to the background". The default behaviour of the BACK button (assuming that you don't override this and provide your own behaviour) is to finish the current Activity. Normally this will take the user to the previous Activity in the current task. In your case, there is no other Activity in the task stack, so the current task is empty. This may appear to the user as "the app is sent to the background", but in reality there is nothing in the background. Your app's task is empty and so it is gone.
When the user then taps the app icon on the HOME screen or taps on the app's task in the list of recent tasks, there is no existing task to bring to the foreground, so the app is started again by creating a new task and launching the root Activity (in your case MainActivity) into the newly created task.
If you want the BACK button to just put your app into the background (instead of calling finish() on SomeActivity, which is the default behaviour) then just override onBackPressed() in SomeActivity and do this instead:
moveTaskToBack(true);
It seems like there is no solution to what I am facing as long as the Activity I want to resume to is not the Launcher activity.
From the the docs:
"Root launcher activities are activities that declare an Intent filter with both ACTION_MAIN and CATEGORY_LAUNCHER. These activities are unique because they act as entry points into your app from the app launcher and are used to start a task.
System behavior on Android 12 and higher
The system moves the activity and its task to the background instead of finishing the activity. This behavior matches the default system behavior when navigating out of an app using the Home button or gesture."
Reading the docs about the new back behavior on Android 12 and onwards tells us that pressing back when you got nothing else on the stack will act as if you pressed the home button.
There's a big exception here, and that is that when you re-open the application, if the one you just popped was not the launcher activity, it will then still need to launch that launcher activity and can't resume from where you left off in a hot state, exactly the reason why I am seeing the splash screen again.
So I think my best bet is to either ignore this for now, or fix my app to be a single-activity app, or at least keep the launcher activity be the top-level one that you exit the app from by pressing back
To indicate a couple of examples, if one wants to experience what I mean, the reproduction steps are to:
Open the app
Press the back button which will send you out of the app to the home screen
Click on the app icon again
As of today, apps like Google Photos, and Google Podcasts don't show the splash again. In contrast, apps like Google Maps, Twitter, Spotify do show the splash again for a brief second.
to call the launcher activity every time you have to clear the stack that means you have to use a flag in your manifest to tell your app not to keep activity in background try android:launchMode="singleTask" inside your activity tag in manifest the activity that you want to be killed everytime you go to background, and as far as how much time splash should be showing you can use timer for that after the timer is finished then your someActivity will be called.
I want to show the same image on the screen non-stop while the 3 activities start one by one : LoadingActivity, MainActivity, and then DetailActivity, and all this time the user sees only one image.
Thus, the picture should hide starting multiple activities in a row. Typically, when the application starts, starting LoadingActivity, then MainActivity, then DetailActivity. Usually it ends at the MainActivity, and while the LoadingActivity is starting , the user sees the Welcomу layout for a few seconds, and then the MainActivity interface. It's very rarely necessary to go to the DetailActivity, for example, when the browser link to the DetailActivity has already been chosen.
But when I get a push notification, I need to go to the DetailActivity immediately bypassing the LoadingActivity and MainActivity, I do not launch multiple services, do not take updates from the server, do not set the required variables, which means that when the user tries to do something from the DetailActivity, the functionality will be limited.
But what I'm trying to say is that I don't like that all 3 interfaces are shown when user presses the push notification, so I decided to ask if it's possible to have only one picture hanging during all these three activities loading?
To get a picture hanging in a row, all three activities, I duplicated to the MainActivity some key points from the LoadingActivity. Push starts MainActivity, if it is push, then I don't show a main layout, but an additional, with a picture. Next, I run websoket, login and query the server for updating data relating to this particular push. I receive data and load the DetailActivity, displaying the right information.
Additionally I made: - in onStop of MainActivity I switch to main layout, so that when user press the back of the DetailActivity, he sees a normal layout of the MainActivity.
- If within 10 seconds do not get a response from the server, then just display DetailActivity without new information. This is not good, but better than to open the MainActivity user may not immediately find the desired option and opens DetailActivity.
I am new to Android Programming.
I want to understand how Activity Stack is maintained for a particular Android Application and how does it changes based on user navigation.
For example, if there are multiple activities then how Activity Stack behaves when user clicks on Back Button or Home Button or launches a new Activity?
I was trying to find a suitable post where I can get all the information, but I did not get any. Can somebody please suggest me some links/posts where I can learn this?
Thanks!
Edited:
Links/Posts that I came across so far:
onSaveInstanceState is not saving my values ( onCreate input Bundle is always null )
Saving Android Activity state using Save Instance State
Android: Launch mode 'single instance'
Do you mean activities and the back stack?
Here is a link:
http://developer.android.com/guide/components/tasks-and-back-stack.html
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack), in the order in which each activity is opened.
The device Home screen is the starting place for most tasks. When the user touches an icon in the application launcher (or a shortcut on the Home screen), that application's task comes to the foreground. If no task exists for the application (the application has not been used recently), then a new task is created and the "main" activity for that application opens as the root activity in the stack.
When the current activity starts another, the new activity is pushed on the top of the stack and takes focus. The previous activity remains in the stack, but is stopped. When an activity stops, the system retains the current state of its user interface. When the user presses the Back button, the current activity is popped from the top of the stack (the activity is destroyed) and the previous activity resumes (the previous state of its UI is restored). Activities in the stack are never rearranged, only pushed and popped from the stack—pushed onto the stack when started by the current activity and popped off when the user leaves it using the Back button. As such, the back stack operates as a "last in, first out" object structure. Figure 1 visualizes this behavior with a timeline showing the progress between activities along with the current back stack at each point in time.
I need this behavior in my app, "user must have to login in each time he tries to launch it(touching the app icon directly, or by task manager or via recent apps)"
So the activities are in this order (think the App name is Foo app)
S - Splash Screen
L - Loading Screen
Lo - Login Screen
M - Main Menu (Has list of tasks)
T - Task Screen
"User starts the app and proceeds as in the following order :
S -> L -> Lo (logs in) -> M (selects one of the Tasks) -> Task Screen
so while he is in the Task Screen, he presses the Home button and the App goes to the background and he uses another app. and lunches the Foo app again. So in this if he pressed the Back button, it shows the previously being used Task Screen??? and if pressed Back button again, it goes to the Main screen again, and so forth...???
This should not happen, simply what I want is, when the App comes to foreground, user needs to login and never be able to go to back to any of the screens.
Note : all the screens have extended a BaseActivity class which has extended the Activity class. and in there I have used onResume(),onPause() method and another custom method to find out when the extended child class goes to pause,and resume when the app comes from the background!!! And in some Screen I have had to use Fragments too...!
Thanks in advance for your time and help!
Add tag android:clearTaskOnLaunch="true" to root (main) activity tag in manifest. Then user will always return to this activity.
To jump to Login from any other activity, use:
public static void logoff(Context c){
Intent logoff = new Intent(c,LoginActivity.class);
logoff.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
c.startActivity(logoff);
}
I am not going to argue why your question describes a not so pleasant user-experience, but what would you say about below solution?
Replace Lo, M and T as fragments. So after L you would have an activity (PostL) where you show first Lo fragment, then on user action you would add on top of Lo an M fragment by get(Supported)FragmentManager.beginTransation().add(id, fragment).commit(), then moving on you would replace M by T: get(Supported)FragmentManager.beginTransation().replace(id, fragment).commit(). - or you can add T on top of M, it's your choice as it might suite better.
In this PostL activity, in onPause() method you would call only get(Supported)FragmentManager.popBackStack() if the fragment backstack is consisted of more than one fragment - the bottom fragment is Lo.
In this way would be covered in both cases when the user taps on Home screen and re-enters the app (no matter from where), or any other app goes in the foreground. Also, if you consider correctly the backstack size, if the user taps back then he will always open the last fragment (that could be M or Lo).
I hope it makes sense ...
This is pretty elaborate, so I'll try and be as clear as possible. Currently, my application needs to reload some data when it is launched. I am loading this data in the onCreate/onRestart methods of my main activity. However, I need to be careful to not reload the data if the user never leaves the current task.
For instance, if I need to start the in-built Contacts application from my main activity to select a contact, then my main activity will pause/stop while I am selecting a new contact. When I return to my main activity, onRestart will be called. In this case, because I have never left the Application's task, I do not want to reload the data. This means some check needs to be included here to determine if the user has come straight back from the Contacts app without ever leaving it.
Otherwise, if the user exits the Contacts app while it was open and re-launched the app from the launcher menu (or recent apps list, etc) - I want to close the Contacts app and reload the data when the Main activity's onRestart method is called.
Is there a way to do this without using any user-frightening permissions (e.g. GET_TASKS). I have been stuck on this problem for a long time now so any help would be GREATLY appreciated :)
Tyvm,
B. Campbell
I learned a lot from the Android docs:
Tasks and Back Stack
Navigation with Back and Up
Application Structure
Here is some highlight:
A task is a collection of activities that users interact with when
performing a certain job. The activities are arranged in a stack (the
"back stack"), in the order in which each activity is opened.
When the current activity starts another, the new activity is pushed
on the top of the stack and takes focus. The previous activity remains
in the stack, but is stopped. When an activity stops, the system
retains the current state of its user interface. When the user presses
the Back button, the current activity is popped from the top of the
stack (the activity is destroyed) and the previous activity resumes
(the previous state of its UI is restored). Activities in the stack
are never rearranged, only pushed and popped from the stack—pushed
onto the stack when started by the current activity and popped off
when the user leaves it using the Back button.
It means when your application pick up a contact, the activity stack in the task might be:
| Activity A in Contacts |
| Activity B in Your App |
| Activity C in other app|
When the user leaves a task by pressing the Home button, the current
activity is stopped and its task goes into the background. The system
retains the state of every activity in the task. If the user later
resumes the task by selecting the launcher icon that began the task,
the task comes to the foreground and resumes the activity at the top
of the stack.
Even if the user exits the Contacts app while it was open and re-launched the app from the launcher menu or recent app list, the activity stack is still like the one we see above. The only possibility that may make your acitity start from another task is another app(might be a launcher app) that start the main activity in a new task by startActivity flags, which is a rare case. In this rare case, you can simply check the uid of the app who start your activity by Binder.getCallinguid() to differentiate it.
EDIT based on comments:
You may check if activity was opened from history by FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY:
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0){
}else{
}
You can also look into:
android:excludeFromRecents if you don't want your app start from recent apps
android:finishOnTaskLaunch
android:stateNotNeeded
If the data you are loading related to the application life-cycle not to a specific activity, you can load it in application class, so it will not be reloaded again unless your application task is killed.