I have an Activity with android:launchMode="singleInstance" . I want launch camera from this and then process result, but startActivityForResult doesn't work with singleInstance. So maybe it's possible to get result from camera using onNewIntent method? If it's possible, how can I implement this?
You might want to look at both singleTask and singleInstance.
singleTask:
The system creates a new task and instantiates the activity at the root of the new task. However, if an instance of the activity already exists in a separate task, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Only one instance of the activity can exist at a time.
Note: Although the activity starts in a new task, the Back button
still returns the user to the previous activity.
singleInstance:
Same as "singleTask", except that the system doesn't launch any other activities into the task holding the instance. The activity is always the single and only member of its task; any activities started by this one open in a separate task.
Note: That means, the activity with launch mode is always in a single
activity instance task. This is a very specialized mode and should
only be used in the applications that are implemented entirely as one
activity.
You could try something like this
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Uri uri = intent.getData();
if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
try {
//load image goes
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
}
Related
I am trying to get results from native activity in React Native Module. When I called startActivityForResult from my React Native Module, onActivityResult is called immediately even though the activity shows up but RESULT_CANCELED is immediately returned to onActivityResult().
I have followed the official guide from here
My Module code to start activity:
Activity currentActivity = getCurrentActivity();
if (currentActivity != null) {
Intent tgIntent = new Intent(currentActivity, SecondActivity.class);
tgIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
currentActivity.startActivityForResult(tgIntent, REQUEST_CODE_ENROLLMENT);
}
I tried both of following to set activity event listener but nothing changes
context.addActivityEventListener(activityEventListener);
context.addActivityEventListener(this);
Remove the "New Task" flag from the intent.
startActivityForResult must be under the same task as the calling activity.
According to documentation:
if the activity you are launching uses Intent#FLAG_ACTIVITY_NEW_TASK, it will not run in your task and thus you will immediately receive a cancel result.
I´ve an app (APP1) which should open other app (APP2) and wait for the result. I´m doing it this way.
private fun startBridgeActivity(fileName: String, isProduction: Boolean) {
val intent = Intent(Intent.ACTION_MAIN)
when (isProduction) {
true -> {
intent.component = ComponentName(
"com.myapp",
"com.myapp.view.ui.ItemSelectionActivity"
)
}
else -> {
intent.component = ComponentName(
"com.myapp.dev",
"com.myapp.view.ui.ItemSelectionActivity"
)
}
}
try {
startActivityForResult(intent, REQUEST_CODE)
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this#MainActivity, "This activity does not exist", Toast.LENGTH_LONG).show()
}
}
Then APP2 receives this intent and opens itself. The launch method on this APP2 it´s just the standard.
The navigation inside APP2 is as follows:
ReceiverActivity - Step1Activity - Step2Activity - LastActivity
I´ve tried setting the result and finishing LastActivity, but it doens't work. Also tried with finishAffinity and finishAndRemoveTasks but they also didn't work.
Then what I did was calling ReceiverActivity from LastActivity and setting there the result and finishing it. But that leaves me on the homescreen and the result wont get to APP1.
val receiverActivityIntent = Intent(this#LastActivity, ReceiverActivity::class.java)
receiverActivityIntent.putExtra("end", true)
TaskStackBuilder.create(this#ReceiverActivity)
.addParentStack(ReceiverActivity::class.java)
.addNextIntent(receiverActivityIntent)
.startActivities()
finish()
I readed some other SO answers where someone wrote that the Activity from APP1 would be added to the stack of APP2 and that might be why the app goes to the homescreen.
Any help will be appreciated.
Thanks.
Your app launches ItemSelectionActivity using startActivityForResult(). Your app will get the callback onActivityResult() when ItemSelectionActivity finishes. It will receive the results that ItemSelectionActivity sets when it calls setResult().
If ItemSelectionActivity needs to launch other activities before it can get the results to send back to your app, you can do this in a few possible ways:
Use FLAG_ACTIVITY_FORWARD_RESULT
When ItemSelectionActivity launches another Activity, it should set the flag Intent.FLAG_ACTIVITY_FORWARD_RESULT in the Intent and call startActvity() (do NOT call startActivityForResult(). The Activity that is being launched must then call setResult() with the results and that data will be passed back to your app. You are basically "forwarding" the request for a result from one Activity to the next. Since you seem to have several activities to go through before you get a result, you can continue to forward the responsibility from one Activity to the next Activity. The last one in the chain should then call setResult() and those results will be passed back to your app in onActivityResult().
Chain startActivityForResult() calls:
When ItemSelectionActivity launches another Activity, it should call startActvityForResult(). The Activity that is being launched must then call setResult() with the results and that data will be passed back to ItemSelefctionActivity in onActivityResult(). ItemSelectionActivity should then itself call setResult() with the data and finish(). The results will be passed back to your app. Since you seem to have several activities to go through before you get a result, you can continue to chain these calls so that each Activity launches the next Activity using starActivityForResult() and the called Activity needs to pass the results back usingsetResult()`.
Have LastActivity deliver the result to ItemSelectionActivity:
This is the solution that you have already tried to implement. LastActivity returns the result directly to ItemSelectionActivity. However, your implementation is broken. DO NOT USE TaskStackBuilder to accomplish this! TaskStackBuilder has a lot of side effects that destroy the Activity stack within the task. What you want to do instead is this:
val receiverActivityIntent = Intent(this#LastActivity, ReceiverActivity::class.java)
receiverActivityIntent.putExtra("end", true)
// add the results to the Intent
receiverActivityIntent.putExtra("results", results)
// Set the CLEAR_TOP and SINGLE_TOP flags (if necessary) to remove any
// activities that are on the stack between ReceiverActivity and LastActivity
receiverActivityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(receiverActivityIntent)
// Calling finish() here isn't necessary if you set the Intent flags
finish()
NOTE: My Kotlin syntax may not be 100% correct, but hopefully you get the idea.
With this solution, the results will be delivered to ReceiverActivity in onNewIntent(). You will need to override onNewIntent(), get the results from the passed Intent and then call setResult() with the results to pass them back to your app.
Suppose I have two activities A and B activity A which contains a button I want to start Activity B when I press Button without intent.
According the Oficial Documentation:
An intent is an abstract description of an operation to be performed. It can be used with startActivity to launch an Activity, broadcastIntent to send it to any interested BroadcastReceiver components, and startService(Intent) or bindService(Intent, ServiceConnection, int) to communicate with a background Service.
An Intent provides a facility for performing late runtime binding between the code in different applications. Its most significant use is in the launching of activities, where it can be thought of as the glue between activities. It is basically a passive data structure holding an abstract description of an action to be performed.
So you have to use it to open activities with no exceptions or workarounds, if you do that, you are ignoring the entire system architecture.
There is no way to start an activity from anotherone without an intent.
If the reason of not using Intent that you don't want the the user to re-enter the previous activity
You can use finish() to finish that activity intent after you done work with
if(currentUser == null){
startActivity(new Intent(MainActivity.this,StartActivity.class));
finish();
}
So user will be unable to back again
If you want to do some code while the activity is finishing
You can use onDestroy() override method, Sometimes it can also be called if the activity is being killed by the android itself so you can add
isFinishing() function
Inside onDestroy() method which checks whether the application is closing by the call finish() returning true or otherwise by anything else returning false then you can easily specify your code for each situation.
#Override
protected void onDestroy() {
super.onDestroy();
if(isFinishing()){
// Activity is being destroyed by the function `finish()`
// What to do...
}else{
// Activity is being destroyed anonymously without `finish()`
// What to do...
}
}
Put your activity inside a Fragment and start the fragment fromo the button.
These are the possible ways to start any Activity
1st
startActivity(new Intent(Activity_A.this, Activity_B.class));
2nd
Intent intent = new Intent(Activity_A.this, Activity_B.class);
startActivity(intent);
3rd
Intent intent = new Intent(Activity_A.this, Activity_B.class);
startActivityForResult(intent,code);
I have an application, which starts with a SplashScreenActivity. Afterwards, a LoginActivity is shown, or if the user is already logged in, a MainActivity is shown. If the application is already running, SplashScreenActivity is dismissed with the following
//SplashScreenActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Adding this check for following cases
if (!isTaskRoot())
{
String intentAction = getIntent().getAction();
if (getIntent().hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
finish();
return;
}
if(getIntent().getCategories().contains(GCMIntentService.INTENT_CATEGORY_GH_NOTIFICATION)){
finish();
return;
}
}
Problem occurs
If I start the application from another activity like PlayStore, it resumes at the right activity if already running. This is the Intent I'm using to reproduce within a second app
//AnotherApplication.apk
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("my.package.name");
startActivity(launchIntent);
However, this action is somehow breaking the Backstack. Instead of closing the application on backpress in the MainActivity, it restarts the application.
//MainActivity.class
#Override
public void onBackPressed() {
if (getNavDrawerMain().isDrawerOpen()) {
getNavDrawerMain().closeDrawer();
} else {
closeApp();
}
}
protected void closeApp() {
if (doubleBackToExitPressedOnce) {
//super.onBackPressed(); //i tried both, but behaviour is the same
finish();
return;
}
this.doubleBackToExitPressedOnce = true;
new Handler().postDelayed(new Runnable() {
#Override
public void run()
doubleBackToExitPressedOnce = false;
}
}, 500);
}
I used breakpoints and found out that MainActivity:onDestroy() get called, but instead of resuming application to the HomeScreen, it always restarts and I don't know why.
I tried the following:
- Used different launchmodes like singleTask and singleInstance, but it didn't make any difference. onNewIntent is called, but if i call finish, HomeActivity restarts
- as commeted below, i tried moveTaskToBack(true), but Activity is restaring too (and we really want to close the app instead of moving it to the BackStack)
Try with moveTaskToBack(true); instead of finish(); to close the app. It will then go to OnRestart() and then OnStart()->OnResume() (and won't go to OnCreate).
And make sure you don't have the "Don't keep activities" marked at Developer Options in your Android Settings (destroy every activity as soon as the user leaves it).
Trying adding this flag to your intent starting your app: RESET_TASK_IF_NEEDED, URL=http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
What it does:
If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task.
You may also use:
http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_REORDER_TO_FRONT
What it does:
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.
Which one you use depend on the desired end result.
If you can't control who starts you you need to set or launch mode to single task or single instance.
Described here:
http://inthecheesefactory.com/blog/understand-android-activity-launchmode/en
The interesting part:
singleTask
This mode is quite different from standard and singleTop. An Activity with singleTask launchMode is allowed to have only one instance in the system (a.k.a. Singleton). If there is an existed Activity instance in the system, the whole Task hold the instance would be moved to top while Intent would be delivered through onNewIntent() method. Otherwise, new Activity would be created and placed in the proper Task.
I have a foreground service that keeps a connection open with the server as long as the user is logged into the application. This is so that the connection is kept alive and can receive messages directly from the server even when the application has been sent into the background by the user pressing Home.
The application has a number of Activities, any of which could be the active one when it is sent into the background.
I would like to allow the user to click on the notification to restore the current Activity. I understand how to restore a particular activity, but wondered if there is a way to restore the last Activity that the user was on? Of course I could keep track of the the last one, and then call that from the Notification callback, but thought there might be a way at a task level?
Thanks for any advice you can offer.
What you need is just a simple Activity that does nothing. Here is an example:
public class NotificationActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Now finish, which will drop the user in to the activity that was at the top
// of the task stack
finish();
}
}
Set up your notification to start this activity. Make sure that in the manifest the task affinity of this activity is the same as the task affinity of the other activities in your application (by default it is, if you haven't explicitly set android:taskAffinity).
When the user selects this notification, if your application is running, then the NotificationActivity will be started on top of the topmost activity in your application's task and that task will be brought to the foreground. When the NotificationActivity finishes, it will simply return the user to the topmost activity in your application (ie: wherever the user left it when it went into the background).
This won't work if your application isn't already running. However, you have 2 options to deal with that:
Make sure the notification isn't present in the notification bar when your application is not running.
In the onCreate() method of the NotificationActivity, check if your application is running, and if it isn't running call startActivity() and launch your application. If you do this, be sure to set the flag Intent.FLAG_ACTIVITY_NEW_TASK when starting the application so that the root activity of the task is not NotificationActivity.
Works very well, thanks David! The following class checks if the application is already running and if not, starts it before finishing (as suggested by David in option 2).
public class NotificationActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// If this activity is the root activity of the task, the app is not running
if (isTaskRoot())
{
// Start the app before finishing
Intent startAppIntent = new Intent(getApplicationContext(), MainActivity.class);
startAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startAppIntent);
}
finish();
}
}
There is a simpler solution that does not require the extra activity. See this post for details. Basically, the notification starts the (possibly existing) task the same way it is started when you click the launcher icon while the app ist in the background.
My solution, which emulates the behaviour of the launcher (bringing up the task to the foreground):
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClassName(MyApplication.class.getPackage().getName(), MainActivity.class.getName());
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
This works, no doubts about it but the problem is when you set your intent as ACTION_MAIN. Then you will not be able to set any bundle to the intent. I mean, your primitive data will not be received from the target activity because ACTION_MAIN can not contain any extra data.
Instead of this, you can just set your activities as singleTask and call your intent normally without setting ACTION_MAIN and receive the intent from onNewIntent() method of your target activity.
But be aware if you call, super.onNewIntent(intent); then a second instance of the activity will be created. Just don't call super method.
I combined David Wasser's and Raginmari's solution by doing that approach to the root activity of your app then it will work for both cases when your app was already started or haven't been started.
public class YourRootActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (!isTaskRoot()) // checks if this root activity is at root, if not, we presented it from notification and we are resuming the app from previous open state
{
val extras = intent.extras // do stuffs with extras.
finish();
return;
}
// OtherWise start the app as usual
}
}