How to start an activity from a singleInstance activity? - android

I have a widget which can pop up small dialogs when clicked. These dialogs are displayed by an activity called RemoteActivity in singleInstance launchMode. In one of these dialogs, there is a button to launch the main app MainActivity, which has the standard launchMode.
However, when this button is clicked, and startActivity() called, MainActivity isn't launched, although I can see the corresponding "Starting activity: Intent { ... }" in logcat.
If I set the launchMode of RemoteActivity to standard then MainActivity gets launched, but this isn't what I want, RemoteActivity is merely an extension of the widget, I don't want it to stack with any other activity.
I also tried with FLAG_ACTIVITY_NEW_TASK but it didn't help, and it shouldn't be necessary anyway according to the docs:
A "singleInstance" activity, on the
other hand, permits no other
activities to be part of its task.
It's the only activity in the task. If
it starts another activity, that
activity is assigned to a different
task — as if FLAG_ACTIVITY_NEW_TASK
was in the intent.
How can I launch my main activity?
UPDATE / ERRATA:
The MainActivity is actually launched but only if it isn't already part of a task. If I launch MainActivity normally through the launcher, and press Back to exit, then RemoteActivity does launch MainActivity.
But if, instead of pressing Back, I press Home to leave MainActivity, then RemoteActivity can't launch MainActivity, although the intent appears in logcat.
I'm testing this on Froyo.
Any idea of what's happening?

Maybe the noHistory flag will work for what you are looking for?

I found the problem: this behavior only occurs when calling finish() before startActivity() in RemoteActivity. If I call startActivity() before finish() then it works fine whether MainActivity is already part of an existing task or not.
Go figure...

Related

How to prevent stacked activity from showing when app is re-launched?

I have an annoying issue with the activity stack that I haven't found a solution for.
Basically I have an activity that acts as a "starter" activity (the main activity in my manifest, this is started from the launcher etc). It is translucent, set using:
android:theme="#android:style/Theme.Translucent.NoTitleBar"
What it does is check the Intent that is fed to it. If the intent data is empty, it starts a new activity which is the main activity for the app.
If the intent data contains certain commands the starter activity should perform certain tasks and then exit, not even starting the main activity. So this should happen without any UI (except for a popup message when done).
My problem is that if the main activity has been started, if the user uses the home button to leave it, the next time the starter activity is started with a command, the main activity also shows up briefly.
I'm assuming this is because of the activity stack since I'm not restarting the main activity from the starter activity in this case.
I've tried various solutions to no avail. I can't use finish() in the main activity in onPause or onStop since that also exits the activity if the user for example enters the settings activity and that is not wanted behavior. I also tried variations of re-launching the starter activity with
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
but that doesn't seem to work either.
The thing that is weird is that when this unwanted behavior happens, onCreate/onResume/onStart is not called on the main activity. Still it shows! I'm guessing this is because it is stacked and since the startup activity is translucent, the main activity is shown through it.
Enable the android:noHistory attribute on your activity within your manifest:
<activity
...
android:noHistory="true">
...
</activity>
This will set the activity to be removed from the activity stack when it starts the next activity. The user will not be able to return to an activity that has android:noHistory="true".
See:
Activity Attribute: android:noHistory

Launch existing singleTask Activity from launcher icon

Scenario:
Activity A (MAIN and LAUNCHER in manifest) starts up when clicking on launcher icon.
In turn it launches Activity B.
Activity B then launches our main app Activity C (MAIN and singleTask in manifest).
Behaviour I require:
Once Activity C has been shown and the home key is then pressed, the next time the launcher icon is pressed I would like to skip straight to Activity C (and not show Activity A (and consequently B) again).
I have tried using FLAG_ACTIVITY_CLEAR_TOP from A, but I still get Activity A whenever I hit icon on launch screen.
Is appearance of my singletask Activity C from launcher achievable?
Update: Use of FLAG_ACTIVITY_CLEAR_TOP from A and not calling finish() creates the situation whereby Activity B appears on press of launcher icon. However, also applying use of FLAG_ACTIVITY_CLEAR_TOP from B and not calling finish() does not resolve situation. now I don't get A on launcher icon press, but get B. What gives!
See similar scenario here.
In your case, I would recommend using a similar approach, where you would have a SharedPreference that would persist a value representing whether your app had previously been launched.
Create a simple Activity that will contain the logic for what you are trying to do here. In that activity's ("pre-A" Activity) onResume() method, check the value of the preference representing whether the app has ran previously.
If it has not previously been ran, launch an intent to Activity A, which calls the subsequent B and C activities; otherwise, launch an intent to Activity C.
Seems pretty straightforward, just remember to define the new "Pre-A" activity in your manifest!

launch external activity w/o it appearing in the back stack

I need to launch an external activity (which isn't mine) for a split second, and then I have a service that launches my previous activity for the user to see.
This works great, but the problem is when the user then presses the back key, he gets to that external activity.
I have two different activity launches here, one for the external activity, and one for my activity to return to (in which I need to use FLAG_ACTIVITY_NEW_TASK because I'm launching it from a service).
For these two launches I've tried any combination of:
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, FLAG_ACTIVITY_NO_HISTORY, FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_CLEAR_TOP, FLAG_ACTIVITY_CLEAR_TASK, FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS.
Nothing helps, I keep seeing the external activity after pressing back.
I've seen some similar questions here, but they all have the middle activity in their own app, so they can call finish on it, or set noHistory="true" in the manifest.
Any ideas?
UPDATE:
Seems like the issue is that I re-launch my previous activity from a service, which means I need to give it FLAG_ACTIVITY_NEW_TASK, from the documentation of Tasks and Back Stack:
FLAG_ACTIVITY_NEW_TASK Start the activity in a new task. If a task is
already running for the activity you are now starting, that task is
brought to the foreground with its last state restored and the
activity receives the new intent in onNewIntent(). This produces the
same behavior as the "singleTask" launchMode value, discussed in the
previous section.
And from the singleTask paragraph:
"singleTask" The system creates a new task and instantiates the activity at the root of the new task
...
Note: Although the activity starts in a
new task, the Back button still returns the user to the previous
activity.
So I need a flag that overrides this behavior somehow...

Problem on launching Activity from other application

I have met a problem, hope you can provide some suggestion on it.
Here is the problem:
There is an A Activity in my application which is the application's MAIN Activity, and can be started from other application(like SMS). Consider now there are A-B-C-D activities in my application's task, as I said before, user can start A Activity from SMS, when A is launched from SMS, I want B C D all to be finished, and the instance of A Activity in the task will be brought to front.
I tried to set A activity's launchMode to singleTask, this will do the trick, but there is another problem: for example, I got A-B-C-D Activities in my application's task, user press HOME key to the home screen, and re-launch the application from the launcher, then A Activity is shown instead of D Activity. It's like the application have been started from the beginning, that is not what I want.
Is there any way that I can do this? Any suggestion will be highly appreciated. Thanks in advance!
Haven't tried it, but you can try this:
Set the activity attribute alwaysRetainTaskState to true.
This should take care to retain all the activities in the stack.
When you want the root activity to be shown (In your case : when launched from SMS - I assume you are invoking the activity from your SMS receiver using start activity), in the intent you pass to startActivity set the FLAG_ACTIVITY_CLEAR_TOP flag.
This should clear off since you explicitly mention it in the Intent.

Is there any way to distinguish between an Android Activity onResume from the home screen?

Is there any way to tell whether an Activity is being resumed (i.e. onResume is called) from the home screen/launcher?
For example, if I have an Application with two activities, A and B.
Scenario 1:
Some user action on Activity A will invoke Activity B, bringing it into the foreground - moving Activity A into the background. As Activity A moves into the background, it goes through onPause() and onStop(). The user (now on Activity B) either finishes the Activity or hits the "back" button, bringing Activity A back to the foreground, causing an onRestart(), onStart(), onResume() sequence.
Scenario 2:
If the user hits the "home" button while Activity A is in the foreground and then re-invokes the Application from the launcher, it goes through the same lifecycle as in Scenario 1. I.e. User hits the "home" button. Activity goes through onPause() and onStop(). User launches the application again, causing Activity A go come back into the foreground, again going through the same onRestart(), onStart(), onResume() sequence as in Scenario 1.
As far as I can tell, the Activity has no way of knowing how it was resumed, it just knows it is being brought back into view. In fact, I have a feeling that there isn't really as much of a concept of an "Application" in Android - in the sense of something that has a single entry and exit point.
in Scenario 2, your activity will get an onNewIntent call, with the launcher intent passed to it.
You could capture the back button press on Activity B and pass an extra value to Activity A. If there is an extra value then the activity was resumed from pressing back on Activity B, if there is no extra value then the Activity was resumed from being hidden.
Could Acitivity A use startActivityForResult() to start Activity B and use onActivityResult() to detect that Activity B finished?
So the straightforward answer to the initial question is probably: no. Launching an activity from the home screen through an icon, or resuming it from the recents screen can not be observed from the intent it is (re)started/resumed with.
Depending on what you are trying to achieve there are some approaches though:
what #superfell suggested:
Check for whether the onNewIntent-method is called on your activity to decide if it was restarted from the launcher. As a precondition you need to set your activity to singleTask/singleTop launchMode in your Manifest:
android:launchMode="singleTask"
depending on what you're trying to achieve, this might be enough! But additionally you might have to deal with what happens when the user presses the back button. Default behavior is to finish & destroy your activity. Thus a brand new copy of it would be launched when it gets selected from the recents screen. Though onNewIntent would not be called, everything would be rebuilt from scratch. If you need to prevent this you can use:
override fun onBackPressed() {
moveTaskToBack(true)
}
finally when you navigate "back" from an activity you launched yourself onNewIntent will also not be called. If you further need to distinguish why your activity is resumed, you might want to start the 2nd activity with startActivityForResult so the onActivityResult is called when you get resumed.
Launcher activity & Intent extra
A completely different approach would be to have a "launcher" activity in your manifest that directly calls your "main" activity and finishes itself. When calling your main activity you can put an intent extra that allows your main activity to distinguish if it was just launched for the first time, or not. As the extra would be present on further onResumes, make sure to overwrite it the first time you "consume" it:
override fun onResume() {
super.onResume()
val firstLaunch = intent.getBooleanExtra(FIRST_LAUNCH, false)
intent.putExtra(FIRST_LAUNCH, false)
if (firstLaunch) {
// do something
}
}
and when starting from your "launcher" activity:
intent.putExtra(FIRST_LAUNCH, true)
startActivity(intent)

Categories

Resources