Skip to content

Commit 00df83c

Browse files
committed
[CB-10795] Exclude current app from external intent list
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. Fixes https://issues.apache.org/jira/browse/CB-10795
1 parent 0db3443 commit 00df83c

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

src/android/InAppBrowser.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@ Licensed to the Apache Software Foundation (ASF) under one
2020

2121
import android.annotation.SuppressLint;
2222
import org.apache.cordova.inappbrowser.InAppBrowserDialog;
23+
import android.content.ComponentName;
2324
import android.content.Context;
2425
import android.content.Intent;
26+
import android.content.pm.PackageManager;
27+
import android.content.pm.ResolveInfo;
28+
import android.os.Parcelable;
2529
import android.provider.Browser;
2630
import android.content.res.Resources;
2731
import android.graphics.Bitmap;
@@ -66,7 +70,9 @@ Licensed to the Apache Software Foundation (ASF) under one
6670
import java.lang.reflect.InvocationTargetException;
6771
import java.lang.reflect.Field;
6872
import java.lang.reflect.Method;
73+
import java.util.ArrayList;
6974
import java.util.HashMap;
75+
import java.util.List;
7076
import java.util.StringTokenizer;
7177

7278
@SuppressLint("SetJavaScriptEnabled")
@@ -353,14 +359,55 @@ public String openExternal(String url) {
353359
intent.setData(uri);
354360
}
355361
intent.putExtra(Browser.EXTRA_APPLICATION_ID, cordova.getActivity().getPackageName());
356-
this.cordova.getActivity().startActivity(intent);
362+
// CB-10795: Avoid circular loops by preventing it from opening in the current app
363+
this.openExternalExcludeCurrentApp(intent);
357364
return "";
358365
} catch (android.content.ActivityNotFoundException e) {
359366
Log.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString());
360367
return e.toString();
361368
}
362369
}
363370

371+
/**
372+
* Opens the intent, providing a chooser that excludes the current app to avoid
373+
* circular loops.
374+
*/
375+
private void openExternalExcludeCurrentApp(Intent intent) {
376+
String currentPackage = cordova.getActivity().getPackageName();
377+
boolean hasCurrentPackage = false;
378+
379+
PackageManager pm = cordova.getActivity().getPackageManager();
380+
List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
381+
ArrayList<Intent> targetIntents = new ArrayList<Intent>();
382+
383+
for (ResolveInfo ri : activities) {
384+
if (!currentPackage.equals(ri.activityInfo.packageName)) {
385+
Intent targetIntent = (Intent)intent.clone();
386+
targetIntent.setPackage(ri.activityInfo.packageName);
387+
targetIntents.add(targetIntent);
388+
}
389+
else {
390+
hasCurrentPackage = true;
391+
}
392+
}
393+
394+
// If the current app package isn't a target for this URL, then use
395+
// the normal launch behavior
396+
if (hasCurrentPackage == false || targetIntents.size() == 0) {
397+
this.cordova.getActivity().startActivity(intent);
398+
}
399+
// If there's only one possible intent, launch it directly
400+
else if (targetIntents.size() == 1) {
401+
this.cordova.getActivity().startActivity(targetIntents.get(0));
402+
}
403+
// Otherwise, show a custom chooser without the current app listed
404+
else if (targetIntents.size() > 0) {
405+
Intent chooser = Intent.createChooser(targetIntents.remove(targetIntents.size()-1), null);
406+
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[] {}));
407+
this.cordova.getActivity().startActivity(chooser);
408+
}
409+
}
410+
364411
/**
365412
* Closes the dialog
366413
*/

0 commit comments

Comments
 (0)