Android - Open an external app and then call an intent URI - android

I have an app 'A'. I am opening another app 'B''s video player and playing a video using an intent URI call like so
String intentURI = "B://this/123";
try {
intent = Intent.parseUri(intentURI, Intent.URI_INTENT_SCHEME);
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
Logger.appendInfoLog("Something went wrong with B", TAG);
e.printStackTrace();
Logger.appendErrorLog(e.getMessage(), TAG);
finish();
}
startActivity(intent);
Now the necessary condition is for that app 'B' to be open in the background for this to work.If the app is closed(killed by Android or manually) or it has crashed,this throws an error.
Is there a way to open that app 'B' first or check its running status and then make the intent URI call. I will not get control back from that app and once I go to the other app I dont have any control on it until the user presses the back button to return to my app.
UPDATE:
I want to start app 'B' first and then call the intent programmatically. Is this possible
UPDATE
I Ran the Catlog app to check what message comes up in app B. It just shows file 123.file (The one i am trying to play in the app B's video player) not found, but when the app is running in the background it goes through fine. It also shows a warning
java.lang.NullPointerException: PrintLn needs a message
and then it says
Activity displayed, but mediaplayer.is not playingVideo
Also the other app is written in flash and packaged as a native app on adobe air

I have an app 'A'. I am opening another app 'B''s video player and playing a video using an intent URI call like so
No, you are not. You can tell that by reading the code -- there is no startActivity() call in that code block.
Now the necessary condition is for that app 'B' to be open in the background for this to work.If the app is closed(killed by Android or manually) or it has crashed,this throws an error.
Then apparently app B has a bug. Please contact the author of app B for assistance.

You can get a list of running apps easily.
ActivityManager manager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> processes = manager.getRunningAppProcesses();
And you can launch apps just as easily.
Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage("com.package....");
startActivity(LaunchIntent);
You can't, however, launch an app into background, so this might not solve your issue.

You don't need to open that app 'B' first. Just check if that app is running with:
// Get running processes
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();
So now you have all the running processes in runningProcesses. Just iterate over the values to find out if your app 'B' is running. An example of this iteration can be found here:
ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
List l = am.getRunningAppProcesses();
Iterator i = l.iterator();
PackageManager pm = this.getPackageManager();
while(i.hasNext()) {
ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo)(i.next());
try {
CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(info.processName, PackageManager.GET_META_DATA));
Log.w("LABEL", c.toString());
}catch(Exception e) {
//Name Not FOund Exception
}
}

Define the intent listener inside of your manifest file.
See the docs for details.
Basically, what you would do is in your AndroidManifest.xml of App B (the app you want to start with the Intent), add a section like:
<receiver android:name=".MyIntentReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Then inside of your MyIntentReceiver class, you would define the code to handle the intent.
public class MyIntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
// handle intent here
}
}

I think your problem is that Intent.parseUri() returns an Intent with the action ACTION_VIEW (see http://developer.android.com/reference/android/content/Intent.html#parseUri%28java.lang.String,%20int%29). And from your code, I gather that android does not recognize that it actually has to start activity B for this. ACTION_VIEW is generic, so id does what seems to be most appropriate. If activity B does not fall into that category (probably not the standard video viewing app), it' will not get launched. I would suggest the following:
Find out how to launch activity B (e.g. Intent i = new Intent.("com.packageB.ActivityB"); or similar. Developer should be able to tell you.
Use i.setData() to set the data of your intent to your file.
Use startActivity()
For step 2, there may be other ways Activity B needs the Uri passed. You can get this information from the developer as well.
Using startActivity rids you of having to check whether App B is running. Something that Android does anyway.

I think it is possible to start another apk from your own, just check this answer. The problem is that if you dont know their uri, it will be hard to do what you propose.
Now you can either ask the developers for the proper names or take a look at your own risk (i don't know about its legality).

Related

How to leave the current app and launch an activity of another app?

I have a simple Android app that needs to launch another app under certain condition, and I will need to check the condition upon the app launch. That is, either continue to launch my app or just launch another app:
if (A == true) {
launch another activity of another app
leave the current app without creating the main activity
} else {
launch the main activity of the current app
}
Could anyone please let me know how to deal with A == true case? I am able to launch another app's activity but I have trouble leaving the current app without even opening the main activity.
Any help will be greatly appreciated!
You can launch other application using following intent
Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage("com.example.abc");//pass the packagename of app you want to open
startActivity( LaunchIntent );
If you don't know the package name of application that you wanted to launch then try your hand on
PackageManager pm;
pm = getPackageManager();
// get a list of installed apps.
packages = pm.getInstalledApplications(0);
Pass the packagename of app you want to open
You can use this if A == true
else You can launch the MainActivity as
startActivity(new Intent(CurrentActivity.this,MainActivity.class));
If you want to start another activity of another app without the normal IntentFilter then the easiest solutions is:
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName("com.anotherapp.package","com.anotherapp.package.MainActivity"));
startActivity(intent);
Of course, you would need to know the package name and the Activity name you want to start
As for finishing your application call
finishAndRemoveTask();

How to gracefully fall back to website when Deep Link can't be handled by app

The situation:
You have an extensive mobile website, m.somewhere.com
On Google Play you have an Android App that duplicates the key features of m.somewhere.com, but not all of them.
Your Client/Employer/Investor has asked you to implement deep-linking for those urls that can be handled by the app.
TL;DR - how do you implement this?
My Approach So Far:
First instinct: match only certain urls and launch for them. Problem: paucity of expression in the AndroidManifest intent-filter prevents this (e.g. http://weiyang.wordpress.ncsu.edu/2013/04/11/a-limitation-in-intent-filter-of-android-application/).
As a subset of the problem, suppose the server at m.somewhere.com knows that any url that ends in a number goes to a certain page on the site, and the marketing guys are constantly futzing with the seo, so e.g.
I want to launch the app for:
http://m.somewhere.com/find/abc-12345
https://m.somewhere.com/shop/xyz-45678928492
But not for
http://m.somewhere.com/find/abc-12345-xyz
https://m.somewhere.com/about-us
no combination of path, pathPrefix, or pathPattern will handle this.
Best practice on stackoverflow (Match URIs with <data> like http://example.com/something in AndroidManifest) seems to be to catch everything, and then handle the situation when you get to onCreate() and realize you shouldn't have handled this particular url:
Android Manifest:
...
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="m.somewhere.com"
android:pathPattern=".*"/>
</intent-filter>
...
Activity onCreate():
Intent i = getIntent()
String action = i.getAction();
Uri uri = i.getData();
if (Intent.ACTION_VIEW.equals(action) && cantHandleUrl(uri)) {
// TODO - fallback to browser.
}
I have programmed something similar to the above that is working, but it leads to a very bad end-user experience:
While browsing m.somewhere.com, there is a hiccup on every url click
while the app is launched and then falls back.
There is a nasty habit for a Chooser screen to popup for each and every link click on m.somewhere.com, asking the user which they would like to use (and the Android App is listed along with the browsers, but clicking on the Android App just launches the chooser screen again). If I'm not careful I get in an infinite relaunch loop for my app (if the user selects "Always"), and even if I am careful, it appears to the user that their "Always" selection is being ignored.
What can be done?
(EDIT: Displaying the site in a WebView in the app for unhandled pages is NOT an option).
Late answer, but for future readers: if you're supporting a minimum of API level 15 then there's a more direct (less hacky) way of falling back to a browser for URLs you realize you don't want to handle, without resorting to disabling/re-enabling URL catching components.
nbarraille's answer is creative and possibly your only option if you need to support APIs lower than 15, but if you don't then you can make use of Intent.makeMainSelectorActivity() to directly launch the user's default browser, allowing you to bypass Android's ResolverActivity app selection dialog.
Don't do this
So instead of re-broadcasting the URL Intent the typical way like this:
// The URL your Activity intercepted
String data = "example.com/someurl"
Intent webIntent = new Intent(Intent.ACTION_VIEW, data);
webIntent.addCategory(Intent.CATEGORY_BROWSABLE);
startActivity(webIntent);
Do this
You would broadcast this Intent instead:
Intent defaultBrowser = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_BROWSER);
defaultBrowser.setData(data);
startActivity(defaultBrowser);
This will tell Android to load the browser app and data URL. This should bypass the chooser dialog even if they have more than one browser app installed. And without the chooser dialog you don't have to worry about the app falling into an infinite loop of intercepting/re-broadcasting the same Intent.
Caveat
You have to be okay with opening the URL (the one you didn't want to handle) in the user's browser. If you wanted to give other non-browser apps a chance to open the link as well, this solution wouldn't work since there is no chooser dialog.
Pitfalls
As far as I can tell, the only quirk from using this solution is that when the user clicks one of your deep links, they'll get to choose to open in your app or their browser, etc. When they choose your app and your internal app logic realizes it's a URL it doesn't want to intercept, the user gets shown the browser right away. So they choose your app but get shown the browser instead.
NOTE: when I say "broadcast" in this answer, I mean the general term, not the actual Android system feature.
There is a somewhat hacky way of doing this:
In the manifest, create an intent-filter for m.somewhere.com, to open a specific deeplink handler activity.
In that Activity, figure out if your app supports that URL or not.
If it does, just open whatever activity
If it doesn't, send a non-resolved ACTION_VIEW intent to be opened by your browser. The problem here, is that your app will also catch this intent, and this will create an infinite loop if your app is selected as the default handler for that URL. The solution is to use PackageManager.setComponentEnabledSetting() to disable your deeplink handler Activity before you send that intent, and re-enable it after.
Some example code:
public class DeepLinkHandlerActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Uri uri = intent.getData();
Intent intent = makeInternallySupportedIntent(uri);
if (intent == null) {
final PackageManager pm = getPackageManager();
final ComponentName component = new ComponentName(context, DeepLinkHandlerActivity.class);
pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
Intent webIntent = new Intent(Intent.ACTION_VIEW);
webIntent.setData(uri);
context.startActivity(webIntent);
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void[] params) {
SystemClock.sleep(2000);
pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
return null;
}
};
task.execute();
} else {
startActivity(intent);
}
finish();
}
}
Hope that helps.
Note: It looks like you need to delay the re-enabling by a couple of seconds for this to work.
Note 2: For a better experience, using a Transparent theme for your activity will make it look like your app didn't even open.
Note 3: If for some reason your app crashes or gets killed before the component re-registers, you're loosing deep link support forever (or until next update/reinstall), so I would also do the component re-enabling in App.onCreate() just in case.
URX provides a free tool (urxlinks.js) that automatically redirects mobile web users into an app if the app is installed. The documentation is available here: http://developers.urx.com/deeplinks/urx-links.html#using-urx-links-js
If two apps are using same scheme then the chooser screen will be popped as android wont know which app the link is intended for. Using custom scheme for your app might solve this issue. But still you can't be sure no one else will use that scheme.
It sounds like you're trying to treat your mobile app and mobile website as extensions of the same experience. That's good practice, generally speaking, but at this point the two are simply not at parity. At least until they reach parity I would not recommend automatically pushing the end user into your mobile app because users who are deliberately using the mobile site in order to find the content your app is missing will find this incredibly frustrating.
Instead, it might make sense to use a smart banner to encourage users on the mobile website pages that do have an in-app equivalent to open the app instead. Those banners would be your deeplinks. You could create them yourself or integrate a tool like Branch ( https://branch.io/universal-app-banner/ ) that handles deep linking and smart banners both.
That last part of your question has to do with where to place the deep links. One advantage to using smart banners instead of redirects is that you can embed them into the appropriate templates on your CMS instead of needing to rely on url detection.
Good luck!
This was my solution to your second problem. PackageManager.queryIntentActivities() will give you the list of apps/activities that would appear in the chooser. Iterate through the list (which should at least include the browser) and find an activity whose package name doesn't match the current app, and set the intent class name to it, then launch an Activity with that intent and call finish();
public Intent getNotMeIntent(Uri uri) {
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
PackageManager manager = context.getPackageManager();
List<ResolveInfo> infos = manager.queryIntentActivities(intent, 0);
for (int i = 0; i < infos.size(); i++) {
ResolveInfo info = infos.get(i);
// Find a handler for this url that isn't us
if (!info.activityInfo.packageName.equals(context.getPackageName())) {
intent.setComponent(null);
intent.setClassName(info.activityInfo.packageName, info.activityInfo.name);
return intent;
}
}
// They have no browser
return null;
}
The Transparent theme (mentioned above) should be a good solution for the first problem.
In destination activity in onCreate set this code for Kotlin:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleIntent(intent)
}
‌private fun handleIntent(intent: Intent?) {
val appLinkAction: String? = intent?.action
val appLinkData: Uri? = intent?.data
showDeepLinkData(appLinkAction, appLinkData)
}
private fun showDeepLinkData(appLinkAction: String?, appLinkData: Uri?) {
if (Intent.ACTION_VIEW == appLinkAction && appLinkData != null) {
val promotionCode = appLinkData.getQueryParameter("exampleQueryString")
Log.e("TAG", "Uri is: $appLinkData")
}
}

recover an app from stack or app in background

I have a small application with two activities.
The first activity has a button, pressing this button opens a second activity (the first is paused)
This second activity launch notification.
With the notification released, leave the application by pressing the "home" button and let the application on the stack or in background.
When I click on the notification, I want the application to retrieve the activity that was on the screen.
I am recovering the application using the package name:
Intent intent = null;
PackageManager manager = getPackageManager();
try {
intent = manager.getLaunchIntentForPackage("hello.com.vierco.pruebas");
if (intent == null)
throw new PackageManager.NameNotFoundException();
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.putExtra("hola", notificationData);;
} catch (PackageManager.NameNotFoundException e) {
}
I get retrieve the application, but always opens the activity number "one" because is reopening.
Someone can tell me how I can retrieve the application? retrieve it from the stack, not open it again. there is any intent?
Wear two days trying without success, I have also searched many forums but without success
A practical example would be grateful because I'm going slightly mad

Start intent on package_removed

I have this code for checking app uninstall:
public void onReceive(Context context, Intent intent){
final String action = intent.getAction();
if("android.intent.action.PACKAGE_REMOVED".equals(action)){
// some action
}
Now I want get the start intent from the uninstalled app.
Is it possible?
Refer to following URLs:
Perform a task on uninstall in android
How to show an Activity BEFORE my app is uninstalled (Android)
The Post by Janusz is very helpful here..
Sadly android at the moment does not give you a possibility to perform code at the moment your app is uninstalled.
All the settings that are set via the SharedPreferences are deleted together with everything in the Aplication Data an Cache folder.
The only thing that will persist is the data that is written to the SD-Card and any changes to phone settings that are made. I don't know what happens to data that is synchronized to the contacts through your app.
I guess the only way to discover this is to test this. You can use the following code to find the launch intent of an application:
final Intent launchIntent = pm.getLaunchIntentForPackage(packageName);
where pm - is PackageManager.
To my point of view this is impossible and you'll receive launchIntent equal to null. But you should check this on your own.

Two instances of my android application are running....How to avoid this?

Here is my problem -
I copied my .apk file onto phone memory card and launch my application clicking on it and it allows me to install my application.I install my application.Finally,I got system installation pop up containing two options "Open" and "Done".When i click "Open" my application got launched.Up to this point everything is working without any problem.
Now in my application I click on a button and some download is taking place as a result(Showing progress dialog).Now I press a Home button,so my application goes to background.
Now I again launch my application by going inside Menu and clicking on my application icon.
Expected result - Still I Should see Progress Dialog for downloading.
Actual result - A new instance/session of my application is getting started.
So how to avoid this so that only one and one instance/session of my application should run.
#Palejandro, here you are. Put the code below into your main activity onCreate() method:
// Possible work around for market launches. See
// http://code.google.com/p/android/issues/detail?id=2373
// for more details. Essentially, the market launches the main activity
// on top of other activities.
// We never want this to happen. Instead, we check if we are the root
// and if not, we finish.
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.w(TAG, "Main Activity is not the root. Finishing Main Activity instead of launching.");
finish();
return;
}
}
I used this piece of code in my projects and it works fine!
I believe you need to put
<activity
android:launchMode="singleInstance"
</activity>
in the manifest file.
what do your OnPause, OnResume and OnCreate?
I will bet money you are not saving anything OnPause, and starting a new instance all the time via OnCreate.
You should read the notes on Activity Lifecycles.
If you haven't got this sorted yet, I would say your app is actually being killed when home is pressed, or perhaps you have a bug that doesn't latch onto whatever object is keeping state.
// put below code in your launcher activity before call super and setcontentview()
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
// get the info from the currently running task
List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(10);
boolean alreadyTask=false;
for(ActivityManager.RunningTaskInfo info : taskInfo){
ComponentName componentInfo = info.topActivity;
String value= componentInfo.getPackageName();
if(value.contains(getPackageName()) && !info.topActivity.getClassName().contains(getPackageName()+".LauncherActivity")){
alreadyTask=true;
Log.i(TAG, "second instance found!!!");
break;
}
}
if(alreadyTask){
finish();
}
I don't have a solution but the problem is that the intent used to start the app is different when you open it directly from install compared to opening it from your home screen. Since it will get started by two different intents it will open a new instance the second time round.
A quick work around is to avoid pressing "Open" when you have installed the application. Press "Done" and then find the application yourself.
See: http://code.google.com/p/android/issues/detail?id=2373

Categories

Resources