Leakcanary: PackageManager.getLaunchIntentForPackage() returns intent for LeakLauncherActivity

Created on 7 May 2020  路  11Comments  路  Source: square/leakcanary

Description

PackageManager.getLaunchIntentForPackage() returns an intent for LeakLauncherActivity

Steps to Reproduce

call PackageManager.getLaunchIntentForPackage()

Activity declaration in AndroidManifest:

<activity
    android:name=".LauncherActivity"
    android:label="@string/app_name"
    >
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

Expected behavior:

Should return an Intent for the same Activity as if LeakCanary was not included

Version Information

  • LeakCanary version: 2.2
  • Android OS version: 10
  • Gradle version: 6.2.2

Additional Information

N/A

help wanted bug

All 11 comments

According to the javadoc:

Returns a "good" intent to launch a front-door activity in a package. This is used, for example, to implement an "open" button when browsing through packages. The current implementation looks first for a main activity in the category Intent#CATEGORY_INFO, and next for a main activity in the category Intent#CATEGORY_LAUNCHER. Returns null if neither are found.

AFAIK we need the MAIN action and LAUNCHER category to have the LeakCanary show up in the launcher, right? So I don't think we can really remove that. I'm not sure how getLaunchIntentForPackage() sorts activities, here's the source:

https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ApplicationPackageManager.java#212

Which lands in this service:

https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/PackageManagerService.java#7236

Which goes here: https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/ComponentResolver.java#232

and that's where activities come from: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/pm/PackageParser.java#3850

So, to summarize: the order of activities is based on the order in which they're defined in AndroidManifest.xml, and when looking at activities with LAUNCHER category, getLaunchIntentForPackage takes the first result from queryIntentActivities

So, I don't really see what LeakCanary can do here, although I welcome any idea.

What you could do:

  • Figure out a way for your launcher activity to be defined before LeakCanary in the manifest
  • Follow the documentation recommendation and add CATEGORY_INFO to your launcher activity, so that it gets picked up first.
  • Disable the leakcanary launcher activity by setting the leak_canary_add_launcher_icon resource bool to false.

You don't need MAIN.

Welp, I tried commenting out <action android:name="android.intent.action.MAIN"/> in the intent filter for the activity alias that LeakCanary enables, and the LeakCanary launcher icon disappeared.

Sure looks like I do need MAIN? Suggestions welcome on what to change here to keep a LeakCanary launcher icon.

Oh nevermind it's DEFAULT that you (obviously) don't need. I would expect that flag to be preferred by the API.

Is the LeakCanary launcher icon important? Isn't it sufficient to just have it as an app shortcut?

Dynamic shortcuts aren't supported on older Android versions, they're less discoverable than launcher icons and there's a max so if an app already has app shorcuts LeakCanary won't add its own.

However, see my previous comment, you can always remove the launcher icon for your own app by redefining the leak_canary_add_launcher_icon boolean res to false.

Wouldn't it be better for default behaviour to not have these kind of impacts? So make the launcher icon and shortcuts opt-in rather than opt-out. And the documentation for those flags will then says things like "enabling launcher icon might break getLaunchIntentForPackage" and "shortcut will only display on api 25+ AND if there is sufficient space in shortcuts popup".

We can certainly document the getLaunchIntentForPackage and dynamic shortcut behaviors.

However, changing the default behavior is a much different story.

First, that change would break all users who have come to rely on having the icon in the launcher. A while ago we tried hiding the icon until leaks were found and that created a lot of confusion.

It's been very helpful for LeakCanary to come with its own launcher icon out of the box because most developers won't bother to set up their own entry point from their app. Yes, that means it can break the behavior of getLaunchIntentForPackage but as far as I know the vast majority of devs don't use this. And when you do, there's an easy work around (disable the launcher or add category info).

As a first time user of LeakCanary, I found the presence of a Leaks launcher icon to be confusing. And then when you get multiple launcher icons all called Leaks (I have different flavors in my app as well as different apps/projects) it gets even more confusing as you have to guess which one is for which app. IMO, a better solution would be to have a separate Leaks app that scans the package manager and provides a list of all the apps using LeakCanary.

In terms of a workaround for this issue, could you add an activity alias of CATEGORY_INFO that points to the real launcher activity?

The CATEGORY_INFO flag has to be added to the activity you want picked. So you need to add that to your manifest, to the activity you want.

You can customize the leakcanary icon and label, see the docs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vincent-paing picture vincent-paing  路  3Comments

matejdro picture matejdro  路  6Comments

BraisGabin picture BraisGabin  路  6Comments

pyricau picture pyricau  路  3Comments

pyricau picture pyricau  路  4Comments