Skip to content

Commit a078337

Browse files
dpolivyjanpio
authored andcommitted
[CB-10795] Exclude current app from external intent list (#154)
On Android, if the app defines an intent-filter for a given URL, and then tries to use inappbrowser to launch that URL via the _system target, the default handler for that intent is the app itself. That behavior can lead to circular loops, and ultimately is not what the developer wants -- the link should be launched in a browser. Because there is no easy way to find the "default" system browser on a device, this solution will do two things: 1) Check if the app is one of the targets for this intent 2) If so, create a custom chooser with all other targets, excluding the current app. If the app is not a target, then the current (existing) behavior is preserved. The only real "downside" to this approach is that a default handler can no longer be set for these URLs within the app, and a chooser will be shown each time the user taps a link that opens in a new browser. Fixes https://issues.apache.org/jira/browse/CB-10795
1 parent cf58b04 commit a078337

File tree

1 file changed

+47
-1
lines changed

1 file changed

+47
-1
lines changed

src/android/InAppBrowser.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ Licensed to the Apache Software Foundation (ASF) under one
1919
package org.apache.cordova.inappbrowser;
2020

2121
import android.annotation.SuppressLint;
22+
import android.content.ComponentName;
2223
import android.content.Context;
2324
import android.content.Intent;
25+
import android.content.pm.PackageManager;
26+
import android.content.pm.ResolveInfo;
27+
import android.os.Parcelable;
2428
import android.provider.Browser;
2529
import android.content.res.Resources;
2630
import android.graphics.Bitmap;
@@ -71,6 +75,7 @@ Licensed to the Apache Software Foundation (ASF) under one
7175
import java.lang.reflect.InvocationTargetException;
7276
import java.lang.reflect.Field;
7377
import java.lang.reflect.Method;
78+
import java.util.ArrayList;
7479
import java.util.Arrays;
7580
import java.util.List;
7681
import java.util.HashMap;
@@ -432,7 +437,8 @@ public String openExternal(String url) {
432437
intent.setData(uri);
433438
}
434439
intent.putExtra(Browser.EXTRA_APPLICATION_ID, cordova.getActivity().getPackageName());
435-
this.cordova.getActivity().startActivity(intent);
440+
// CB-10795: Avoid circular loops by preventing it from opening in the current app
441+
this.openExternalExcludeCurrentApp(intent);
436442
return "";
437443
// not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it
438444
} catch (java.lang.RuntimeException e) {
@@ -441,6 +447,46 @@ public String openExternal(String url) {
441447
}
442448
}
443449

450+
/**
451+
* Opens the intent, providing a chooser that excludes the current app to avoid
452+
* circular loops.
453+
*/
454+
private void openExternalExcludeCurrentApp(Intent intent) {
455+
String currentPackage = cordova.getActivity().getPackageName();
456+
boolean hasCurrentPackage = false;
457+
458+
PackageManager pm = cordova.getActivity().getPackageManager();
459+
List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
460+
ArrayList<Intent> targetIntents = new ArrayList<Intent>();
461+
462+
for (ResolveInfo ri : activities) {
463+
if (!currentPackage.equals(ri.activityInfo.packageName)) {
464+
Intent targetIntent = (Intent)intent.clone();
465+
targetIntent.setPackage(ri.activityInfo.packageName);
466+
targetIntents.add(targetIntent);
467+
}
468+
else {
469+
hasCurrentPackage = true;
470+
}
471+
}
472+
473+
// If the current app package isn't a target for this URL, then use
474+
// the normal launch behavior
475+
if (hasCurrentPackage == false || targetIntents.size() == 0) {
476+
this.cordova.getActivity().startActivity(intent);
477+
}
478+
// If there's only one possible intent, launch it directly
479+
else if (targetIntents.size() == 1) {
480+
this.cordova.getActivity().startActivity(targetIntents.get(0));
481+
}
482+
// Otherwise, show a custom chooser without the current app listed
483+
else if (targetIntents.size() > 0) {
484+
Intent chooser = Intent.createChooser(targetIntents.remove(targetIntents.size()-1), null);
485+
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[] {}));
486+
this.cordova.getActivity().startActivity(chooser);
487+
}
488+
}
489+
444490
/**
445491
* Closes the dialog
446492
*/

0 commit comments

Comments
 (0)