Start two activities from Android application - java

I have an application A that wants to start an Activity in another application, B, which I don't own and cannot edit.
If B is already running and visible in recent apps, there's no problem in executing the wanted Activity of B using an Intent.
If B isn't running, I use the following code to execute its main Activity first, and then the one I want to execute:
String bPackage = "com.example.applicationb";
PackageManager pm = getPackageManager(this);
Intent main = pm.getLaunchIntentForPackage(bPackage);
Intent wanted = new Intent();
wanted.setPackage(bPackage);
wanted.setComponent(new ComponentName(bPackage,bPackage+".WantedActivity"));
main.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
wanted.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
wanted.setExtras(mPreviouslyCreatedBundle);
startActivity(main);
startActivity(wanted);
The wanted Activity executes, but after some seconds I get an error and it stops working. Am I setting the Intents in a wrong way?

Make sure u have setted exported="true" for activity you are trying to redirect to another package
Basically my idea is when your second paackage app leave u need :
android.os.process.killprocess(android.os.process.mypid())
and when you launch use flag as start new task :
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

Related

Android: Uri Access Life Time from one activity to another

I have a helper method for choosing images and videos, let's call it Activity B.
So, this is how it works:
// were in Activity A
// user wants to choose a video
startActivityB(callbacks);
------------------------------------
// were in Activity B now
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("video/*");
chooseVideoLauncher.launch(intent);
------------------------------------
// were in chooseVideoLauncher now
Uri videoURI = ...;
callbacks.passVideoURI(videoURI); // this way, Activity A gets the videoURI
// do some more things...
finish(); // so the helper activity B is finished now, as the video is chosen already
------------------------------------
// were in activity A again, but now we have the videoURI
// user clicked a button: "Upload video"
uploadVideo(videoURI);
This is the error I get:
java.lang.SecurityException: Permission Denial: opening provider com.miui.gallery.provider.GalleryOpenProvider from ProcessRecord{f5899ab 29899:com.xxx} (pid=xxx, uid=xxx) that is not exported from UID xxx
I have googled the error and found this SO thread: here
#CommonsWare explains the error in a comment and links his blog post: Uri Access Lifetime: Shorter Than You Might Think
So the error happens because the helper Activity B chose the file, so the access is tied to Activity B. No other activity has access, and as soon as Activity B is destroyed (what happens in my code), the access to videoURI is completely gone. So when I later try to upload the video, it throws this error.
I tried these solutions:
Create a local copy of the video and pass that copy to Activity A. This works, but is a bad solution. For longer videos the app crashes with a memory overflow. So it's not an option.
Setting the flags #CommonsWare mentioned. So the code looks like this:
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("video/*");
intent.setFlags(FLAG_GRANT_READ_URI_PERMISSION);
intent.setFlags(FLAG_GRANT_WRITE_URI_PERMISSION);
chooseVideoLauncher.launch(intent);
But this doesn't seem to change anything. The error message remains exactly the same. Am I setting them wrong?
#CommonsWare says using a service would also be a solution. I would prefer not to create a service purely for fixing this permission error. If there's no other solution, I will of course.
But is there no way to grant Activity A permission to that Uri as well?
The best solution, by far, is to combine Activity A and Activity B into a single activity. Use fragments or composables for separate screens.
Setting the flags #CommonsWare mentioned. So the code looks like this:
You would not set the flags on the ACTION_PICK Intent. Instead, Activity B needs to start Activity A (in addition to finish()). You would put the Uri into the "data" facet of the Intent (e.g., via setData()), and you would put the flags on that Intent. You would also need something like FLAG_ACTIVITY_REORDER_TO_FRONT or something to avoid having two copies of Activity A on the back stack.
For the second way Set the flags to get the long lifetime, you can try to add the following code, to get the permission, then start the activity
val contentResolver = applicationContext.contentResolver
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
// Check for the freshest data.
contentResolver.takePersistableUriPermission(uri, takeFlags)

My app skips starting an intent and executes the next code lines?

So I have the following currently:
public void addProject(View v) {
String pn = "";
Bundle extras = new Bundle();
Intent i = new Intent(mMainContext, AddProject.class);
mMainContext.startActivity(i, extras);
if(extras.getString("projectName") != null) {
pn = extras.getString("projectName");
}
}
This is just an example, the first bit is correct though the if statement may not be but this isn't about that block.
What I have noticed is that if I place breakpoints on the new intent, start activity and then the if statement,
Android seems to skip past the startActivity and go onto the if statement.
If I then remove the if statement and run it again, it then launches the intent.
So what I've realised is that it runs to the end of the addProject block and then actually launches the intent...
Why is this? And more importantly, how I do stop it doing that?
Intended outcome is as follows:
1) user presses "Add Project" button
2) intent launched and user inputs project name and presses submit
3) calling intent then receives the p name for use later in the function.
Why is this?
startActivity() is asynchronous. When it returns, the activity is not yet started.
how I do stop it doing that?
You can't.
Intended outcome is as follows: 1) user presses "Add Project" button 2) intent launched and user inputs project name and presses submit 3) calling intent then receives the p name for use later in the function.
Having separate activities may not be the best plan for this (compared with using fragments).
If you wish to use activities, the most natural approach is:
Use startActivityForResult() instead of startActivity()
Have AddProject use setResult() to create a result Intent to deliver back to your first activity
Have your first activity override onActivityResult(), where it can receive a copy of that result Intent and get the results from it
Note that this still will all happen after addProject() returns. However, you will (eventually) get your results.
The Android documentation does not cover this very well, but this is what it has on the subject.
you must use onActivityResult for such task.
From your main Activity use: startActivityForResults(intent) in place
of startActivity();
In your AddProject Activity when user inputs project name and press the
button .
Intent i = new Intent();
i.putExtra("projectName",userInputTextFromEditText);
setResults(RESULT_OK,i)
In your MainActivity
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data){
pn = intent.getExtras.getStringExtra("projectName","");
}
this way you will find your result

Initiliazing ParseLoginUI?

Where does one actually place the code to launch the ParseLoginUI activity?
ParseLoginBuilder builder = new ParseLoginBuilder(MainActivity.this);
startActivityForResult(builder.build(), 0);
Is it in the ParseLoginDispatchActivity? This was not made very clear at all within any of the official documentation:
https://github.com/ParsePlatform/ParseUI-Android
https://www.parse.com/docs/android/guide#user-interface
I'm importing ParseLoginUI into my existing app. What do I once I've installed everything, updated my manifests, my build.gradle and now want to actually launch the Login activity once my app launches?
Do I put something in my manifest to indicate that the ParseLoginActivity should launch first? That doesn't seem to work as an Activity from my main application is required to launch as the initial intent. I'm a little lost here... Any thoughts?
Well I did find one solution, albeit a trivial one:
Intent loginIntent = new Intent(MainActivity.this, ParseLoginActivity.class); startActivity(loginIntent);
I launched the above Intent with an options menu item, but you could do it with a button or whatever else suits your needs.
If you're importing ParseLoginUI into an existing app, it appears you can just launch ParseLoginActivity with a simple Intent. I wish they mentioned this on their integration tutorial. Seems like the most straightforward way to get it running.
This solution definitely launches the Activity you want, but it doesn't check for whether the user is logged in or not and hence doesn't redirect you to the appropriate pages in your log-in flow (which I believe has more to do with your Manifest). It does, however, allow you to successfully register a user and log in with Parse, which is a great start.
A better solution would be to add the following to the onCreate method in the Activity that launches when your app launches. So if when your app launches you land on FirstActivity, the following will check to see if you are logged in. If you are not, you will be sent the login screen, and if you are logged in you will be sent to the second Activity, which is presumably where your users will want to be when they open your app.
ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser != null) {
Intent launchMainActivity = new Intent(this, SecondActivity.class);
startActivity(launchMainActivity );
} else {
ParseLoginBuilder builder = new ParseLoginBuilder(FirstActivity.this);
startActivityForResult(builder.build(), 0);
}

Android: OutOfMemory error and the backstack

the following sheet represents the working flow in the application this question is about.
I ran into problems with OutOfMemory Errors, mostly because users were able to switch from activity B to activity D multiple times (They are showing different content for every attempt), without the previous activity being destroyed. This led into a very large backstack resulting in an OutOfMemory error.
To avoid this, I followed the recommendation to add parent activites to the manifest and create new backstacks with the TaskStackBuilder class:
void openDFromB()
{
Intent i = new Intent(this, ActivityD.class);
TaskStackBuilder.create(this)
.addParentStack(ActivityD.class)
.addNextIntent(i)
.startActivities();
}
If a user now switches from activity B to D, MainMenu, A and B are destroyed and a new Backstack (MainMenu, C, D) is created.
This solves my memory problems for api level greater 10, but unfortunately the TaskStackBuilder does not create backstacks for devices pre api 11.
Any idea what to do in order to avoid users stacking infinite amounts of activities prior api 11? Is this even possible or should I try to free as many resources as possible in onPause to gain a maximum amount of stacked activities before OoM?
thanks in advance!
Try this-
Intent intent = new Intent(getApplicationContext(),yourActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
Another solution is-
Setting android:noHistory="true" on the activity in your manifest file will remove an activity from the stack whenever it is navigated away from.
But the most appropriate way is to call finish() method whenever you start new activity like in the following sample code-
Intent intent = new Intent(this, yourActivity.class);
startActivity(intent);
finish();
You can also create custom Broadcast receiver and register it in every activity which can be fired on event of your choice. For more help you can check this link.
Zabri answer is good, but i would propose another probably is better:
You dont close every activity, but when you are opening a new activity that has been open before, you tell the system that it can clear the way back and reopen that one, of course with a new intent and all the new data that you need.
So, if you do A - B - D - B - D - B, instead of having that big stack,you will have
A - B
A - B - D
A - B
A - B - D
A - B
So, if you do C - D - B - D - B - D, your stack will be
C - D
C - D - B
C - D
C - D - B
C - D
and so on.
In this way, i think the stack actually reflects what the user can have in mind, and also navigation is very logic, you don't even need to captre back button to have the navigation making sense, (of course you can do it if you want a more specific user experience)
The code to achieve this, is:
create a function that receives a class (something like ActivityD.class), there you create an intent, with the flags Intent.FLAG_ACTIVITY_CLEAR_TOP and Intent.FLAG_ACTIVITY_NEW_TASK and then when you need to open an activity, just call this function. You dont care about if the activity is in the stack or not. If it isnt,it is created normally.
protected void startActivity(Class<?> clase) {
final Intent i = new Intent(this, clase);
int flags = Intent.FLAG_ACTIVITY_CLEAR_TOP;
flags |= Intent.FLAG_ACTIVITY_NEW_TASK;
i.setFlags(flags);
startActivity(i);
}
startActivity(ActivityD.class);
May be your application having memory leaks and due to that older activities not getting reclaim by the GC and eventually getting heap size increase every-time activity switch occurs.
How to find leaks?
Please watch: http://www.youtube.com/watch?v=_CruQY55HOk
References:
http://android-developers.blogspot.in/2011/03/memory-analysis-for-android.html.
http://kohlerm.blogspot.in/2009/07/eclipse-memory-analyzer-10-useful.html
I don't know how it works with the Navigation Drawer, but I was creating an Android application some time ago which started with a loading screen and then moved to the main activity of the app. Since I didn't need/want the user ever to go back to the loading screen, I just called finish() before moving to the main activity. Like this:
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
And then, when the user pressed the back button from the main activity, they didn't get to the loading activity, but to wherever they were even before that.
So you could also try to do this:
void openDFromB()
{
Intent i = new Intent(this, ActivityD.class);
startActivity(i);
finish();
}
As I said, I have never implemented a Navigation Drawer, but something tells me that doing this might break its whole behaviour. Also, calling finish() in an activity is such a basic concept that I would be surprised if you hadn't tried it already. :) But still, I am posting it as a shot in the dark, just in case it helps you. Good luck. :)
Hello you can try to add the following statement in your manifest file under application tag:
android:largeHeap="true"
Also make sure your android:targetSdkVersion is 14.

Start android app from sleeping device (not own activity)?

I'd like to start an app from a sleeping device.
First i do a wakelock to wakeup screen. But i cant get the device to unlock?
I know i can start my own activity with something like:
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
but as i'd like to start an 3rd party app app i cant use getWindow():
mContext.startActivity(mContext.getPackageManager().getLaunchIntentForPackage("com.sec.android.app.xy"));
Is there any way to set the flags before starting the activity?
If you know the third party's package and launcher activity names , this code should work (mPackage and mActivityName are those strings):
Intent LaunchIntent = new Intent();
LaunchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
LaunchIntent.setClassName(mPackage, mPackage + "." + mActivityName);
mContext.startActivity(LaunchIntent);
and mContext is the original application context (which you can instantiate as Context mContext = this.getApplicationContext();).

Categories