getLaunchIntentForPackage is null for some apps
Asked Answered
A

5

51

I'm building a service that sends a list of installed apps from an Android TV or a Fire TV to a mobile phone. The phone then sends back the package name of the app it wants to launch and the service launches it.

This is the code that creates the list

public List<InstalledApp> GetInstalledApps(boolean isAndroid) {
    PackageManager pm = getPackageManager();
    List<ApplicationInfo> allPackages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
    List<InstalledApp> userPackages = new ArrayList<InstalledApp>();

    for (ApplicationInfo packageInfo : allPackages) {

        if (isSystemPackage(packageInfo)) continue;

        InstalledApp app = new InstalledApp();
        app.setPackageName(packageInfo.packageName);
        app.setAppName(pm.getApplicationLabel(packageInfo).toString());
        if (!isAndroid) {
            app.setIcon(pm.getApplicationIcon(packageInfo));
        }
        app.setAccentColor(getAccentColor(pm.getApplicationIcon(packageInfo)));


        userPackages.add(app);
    }

    return userPackages;
}

This is how I launch the apps

public void launchApp(String packageName) {
    PackageManager pm = getPackageManager();
    Intent intent = pm.getLaunchIntentForPackage(packageName);
    startActivity(intent);
}

On the Fire TV everything works perfectly but on the Android TV the intent for many of the apps is always null. These are just a few.

  • com.haystack.android
  • com.netflix.ninja
  • tv.pluto.android
  • com.bamnetworks.mlbtv

However with the same code, these apps work just fine.

  • com.hulu.livingroomplus
  • com.sling
  • com.frogmind.badland
  • com.songza.tv

Could anyone provide any insight on what I might be doing wrong?

Thanks!

EDIT: I've also tried this and I get the exception

android.content.ActivityNotFoundException: No Activity found to handle Intent { cat=[android.intent.category.LEANBACK_LAUNCHER] flg=0x10000000 pkg=com.netflix.ninja }

public void launchApp(String packageName) {
    Intent intent = new Intent();
    intent.setPackage(packageName);
    intent.addCategory("android.intent.category.LEANBACK_LAUNCHER");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
}

EDIT 2:

This is the code that works for me:

public void launchApp(String packageName) {
    Intent intent = new Intent();
    intent.setPackage(packageName);

    PackageManager pm = getPackageManager();
    List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
    Collections.sort(resolveInfos, new ResolveInfo.DisplayNameComparator(pm));

    if(resolveInfos.size() > 0) {
        ResolveInfo launchable = resolveInfos.get(0);
        ActivityInfo activity = launchable.activityInfo;
        ComponentName name=new ComponentName(activity.applicationInfo.packageName,
                activity.name);
        Intent i=new Intent(Intent.ACTION_MAIN);

        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        i.setComponent(name);

        startActivity(i);
    }
}
Accumbent answered 25/5, 2015 at 21:10 Comment(7)
Bear in mind that on Android TV, LEANBACK_LAUNCHER is the category for the launcher, instead of LAUNCHER as it is on phones and tablets. My guess is that those apps do not have a LAUNCHER activity and that getLaunchIntentForPackage() only works for LAUNCHER, not LEANBACK_LAUNCHER.Toothless
I tried manually setting the package name for the intent and adding the category android.intent.category.LEANBACK_LAUNCHER but it still doesn't work. I know for sure that Netflix has a LEANBACK_LAUNCHER. I'm targeting SDK versions 17-21 if that matters.Accumbent
"I tried setting the package name for the intent and adding a the category android.intent.category.LEANBACK_LAUNCHER but it still doesn't work" -- I don't know what you mean by that in the context of your existing code. You are welcome to use queryIntentActivities() to find all LEANBACK_LAUNCHER activities. That (albeit with LAUNCHER) is what home screens do, not use getLaunchIntentForPackage()). Here is an example home screen-style launcher: github.com/commonsguy/cw-omnibus/tree/master/Introspection/…Toothless
Thank you so much. Could you please make this an answer and I'll accept it?Accumbent
Have you tried using getLeanbackLaunchIntentForPackage()?Wastepaper
Thank you very much for Edit 2! I have build a launcher and failed to start another launcher from that because pm.getLaunchIntentForPackage returned null. Your code works like a charm on that issueMadwort
This code worked for me, But I didn't describe Leanback launcher explicitly. Simply adding into the manifest file worked for me.Pinole
T
25

To create a home screen-style launcher, don't look for apps and then try to get launch Intents for each. Look for launchable activities, using queryIntentActivities() on PackageManager.

For example, this activity (from this sample project) implements a home screen-style launcher using this technique:

/***
  Copyright (c) 2008-2012 CommonsWare, LLC
  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  use this file except in compliance with the License. You may obtain a copy
  of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
  by applicable law or agreed to in writing, software distributed under the
  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
  OF ANY KIND, either express or implied. See the License for the specific
  language governing permissions and limitations under the License.

  From _The Busy Coder's Guide to Android Development_
    http://commonsware.com/Android
*/

package com.commonsware.android.launchalot;

import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.util.Collections;
import java.util.List;

public class Launchalot extends ListActivity {
  AppAdapter adapter=null;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    PackageManager pm=getPackageManager();
    Intent main=new Intent(Intent.ACTION_MAIN, null);

    main.addCategory(Intent.CATEGORY_LAUNCHER);

    List<ResolveInfo> launchables=pm.queryIntentActivities(main, 0);

    Collections.sort(launchables,
                     new ResolveInfo.DisplayNameComparator(pm)); 

    adapter=new AppAdapter(pm, launchables);
    setListAdapter(adapter);
  }

  @Override
  protected void onListItemClick(ListView l, View v,
                                 int position, long id) {
    ResolveInfo launchable=adapter.getItem(position);
    ActivityInfo activity=launchable.activityInfo;
    ComponentName name=new ComponentName(activity.applicationInfo.packageName,
                                         activity.name);
    Intent i=new Intent(Intent.ACTION_MAIN);

    i.addCategory(Intent.CATEGORY_LAUNCHER);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    i.setComponent(name);

    startActivity(i);    
  }

  class AppAdapter extends ArrayAdapter<ResolveInfo> {
    private PackageManager pm=null;

    AppAdapter(PackageManager pm, List<ResolveInfo> apps) {
      super(Launchalot.this, R.layout.row, apps);
      this.pm=pm;
    }

    @Override
    public View getView(int position, View convertView,
                          ViewGroup parent) {
      if (convertView==null) {
        convertView=newView(parent);
      }

      bindView(position, convertView);

      return(convertView);
    }

    private View newView(ViewGroup parent) {
      return(getLayoutInflater().inflate(R.layout.row, parent, false));
    }

    private void bindView(int position, View row) {
      TextView label=(TextView)row.findViewById(R.id.label);

      label.setText(getItem(position).loadLabel(pm));

      ImageView icon=(ImageView)row.findViewById(R.id.icon);

      icon.setImageDrawable(getItem(position).loadIcon(pm));
    }
  }
}

On an Android TV device, you should also search for LEANBACK_LAUNCHER activities, as that's what Android TV uses, and TV-specific APKs might not have a regular LAUNCHER activity, or at best have one that is not necessarily ideal for use on a TV.

Toothless answered 25/5, 2015 at 22:6 Comment(5)
I tried your answer modified, here but it failed. Can you please suggest an answer? #38856592Ejaculate
Thank you very much! I have build a launcher and failed to start another launcher from that because pm.getLaunchIntentForPackage returned null. Your code works like a charm on that issue.Madwort
Why set component together with action and category on intent used to start an activity (in onListItemClick()) ? Setting a component will make intent explicit, there's no need for action nor category.Discussion
@PiotrŚmietana: The activity that we are starting might be looking at actions or categories to determine what to do. Sometimes, the developer has multiple <intent-filter> elements on the <activity> and needs to examine the incoming Intent to distinguish between the various options. Hence, it is safest if you create an Intent that will match an <intent-filter> for the activity and set the component. That way, the explicit Intent is guaranteed to go where you want, but the activity gets the implicit action+category that it might need.Toothless
This is actually what getLaunchIntentForPackage does under the hood anyway: android.googlesource.com/platform/frameworks/base/+/refs/heads/… The only difference is it calls setPackage on the Intent (which you called main) in order to ensure that the queried intents are only those for the specific package which is required.Kellam
W
74

Since Android 11 there is a behavior change that some apps will not provide this info unless you add queries tag to the AndroidManifest like

    <queries>
        <package android:name="app.i.want.to.query" />
    </queries>

Refer here for more info

Wilfredowilfrid answered 4/12, 2020 at 8:58 Comment(6)
This is the secret none of the tutorials mentionSpirochete
I entered this text in the manifest, now it gives an error and it is not deletedEleonoreleonora
don't do this !Eleonoreleonora
This worked actually for me. "If your app targets Android 11 (API level 30) or higher and needs to interact with apps other than the ones that are visible automatically, add the <queries> element in your app's manifest file. Within the <queries> element, specify the other apps by package name."Boccioni
More info - android-developers.googleblog.com/2020/07/…Quarrelsome
You need to add the package name inside and please make sure you add the this tag directly inside manifest tag.Sanitarium
K
31

In Android 11 you can only get a limited number of package names using getInstalledApplications() and you only get intent for some of them by using getLaunchIntentForPackage().

Add this permission to your Android Manifest file to get all application package names and intent.

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

If you want intent for only a limited number of apps you should instead use queries in your android manifest file.

<queries>
    <package android:name="packageName" />
</queries>
Korwin answered 2/7, 2021 at 11:55 Comment(1)
If you publish your app on Google Play with QUERY_ALL_PACKAGES, your app's use of this permission is subject to approval. developer.android.com/training/…Gaeta
T
25

To create a home screen-style launcher, don't look for apps and then try to get launch Intents for each. Look for launchable activities, using queryIntentActivities() on PackageManager.

For example, this activity (from this sample project) implements a home screen-style launcher using this technique:

/***
  Copyright (c) 2008-2012 CommonsWare, LLC
  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  use this file except in compliance with the License. You may obtain a copy
  of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
  by applicable law or agreed to in writing, software distributed under the
  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
  OF ANY KIND, either express or implied. See the License for the specific
  language governing permissions and limitations under the License.

  From _The Busy Coder's Guide to Android Development_
    http://commonsware.com/Android
*/

package com.commonsware.android.launchalot;

import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.util.Collections;
import java.util.List;

public class Launchalot extends ListActivity {
  AppAdapter adapter=null;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    PackageManager pm=getPackageManager();
    Intent main=new Intent(Intent.ACTION_MAIN, null);

    main.addCategory(Intent.CATEGORY_LAUNCHER);

    List<ResolveInfo> launchables=pm.queryIntentActivities(main, 0);

    Collections.sort(launchables,
                     new ResolveInfo.DisplayNameComparator(pm)); 

    adapter=new AppAdapter(pm, launchables);
    setListAdapter(adapter);
  }

  @Override
  protected void onListItemClick(ListView l, View v,
                                 int position, long id) {
    ResolveInfo launchable=adapter.getItem(position);
    ActivityInfo activity=launchable.activityInfo;
    ComponentName name=new ComponentName(activity.applicationInfo.packageName,
                                         activity.name);
    Intent i=new Intent(Intent.ACTION_MAIN);

    i.addCategory(Intent.CATEGORY_LAUNCHER);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    i.setComponent(name);

    startActivity(i);    
  }

  class AppAdapter extends ArrayAdapter<ResolveInfo> {
    private PackageManager pm=null;

    AppAdapter(PackageManager pm, List<ResolveInfo> apps) {
      super(Launchalot.this, R.layout.row, apps);
      this.pm=pm;
    }

    @Override
    public View getView(int position, View convertView,
                          ViewGroup parent) {
      if (convertView==null) {
        convertView=newView(parent);
      }

      bindView(position, convertView);

      return(convertView);
    }

    private View newView(ViewGroup parent) {
      return(getLayoutInflater().inflate(R.layout.row, parent, false));
    }

    private void bindView(int position, View row) {
      TextView label=(TextView)row.findViewById(R.id.label);

      label.setText(getItem(position).loadLabel(pm));

      ImageView icon=(ImageView)row.findViewById(R.id.icon);

      icon.setImageDrawable(getItem(position).loadIcon(pm));
    }
  }
}

On an Android TV device, you should also search for LEANBACK_LAUNCHER activities, as that's what Android TV uses, and TV-specific APKs might not have a regular LAUNCHER activity, or at best have one that is not necessarily ideal for use on a TV.

Toothless answered 25/5, 2015 at 22:6 Comment(5)
I tried your answer modified, here but it failed. Can you please suggest an answer? #38856592Ejaculate
Thank you very much! I have build a launcher and failed to start another launcher from that because pm.getLaunchIntentForPackage returned null. Your code works like a charm on that issue.Madwort
Why set component together with action and category on intent used to start an activity (in onListItemClick()) ? Setting a component will make intent explicit, there's no need for action nor category.Discussion
@PiotrŚmietana: The activity that we are starting might be looking at actions or categories to determine what to do. Sometimes, the developer has multiple <intent-filter> elements on the <activity> and needs to examine the incoming Intent to distinguish between the various options. Hence, it is safest if you create an Intent that will match an <intent-filter> for the activity and set the component. That way, the explicit Intent is guaranteed to go where you want, but the activity gets the implicit action+category that it might need.Toothless
This is actually what getLaunchIntentForPackage does under the hood anyway: android.googlesource.com/platform/frameworks/base/+/refs/heads/… The only difference is it calls setPackage on the Intent (which you called main) in order to ensure that the queried intents are only those for the specific package which is required.Kellam
A
2

I got the same error when calling getLaunchIntentForPackage(packageName). It was fixed by adding this in the launcher activity's intent-filter tag in the manifest file.

<category android:name="android.intent.category.LAUNCHER" />

When creating a new TV application in Android Studio, it didn't have the above as the default, instead it has this as the default in the launcher activity's intent-filter tag in the manifest file.

<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
Appetizing answered 10/4, 2019 at 14:36 Comment(0)
C
-1
    Intent launchIntent = null;

                try{
                        launchIntent = context.getPackageManager().getLeanbackLaunchIntentForPackage(pkgName);
                    } catch (java.lang.NoSuchMethodError e){
                    }

                    if (launchIntent == null) launchIntent = context.getPackageManager().getLaunchIntentForPackage(pkgName);

                if (launchIntent != null)  {
                    launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    context.startActivity(launchIntent);
                } else {
                   // failure message
                }
Counterproductive answered 26/5, 2015 at 17:34 Comment(1)
Good point, nice to have your answer here. My app is targeting Android TV and I was struggling about it until now. You clear things out. ThanksKun

© 2022 - 2024 — McMap. All rights reserved.