47

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);
    }
}
Jeremy Roberts
  • 751
  • 1
  • 5
  • 13
  • 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`. – CommonsWare May 25 '15 at 21:14
  • 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. – Jeremy Roberts May 25 '15 at 21:19
  • 1
    "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: https://github.com/commonsguy/cw-omnibus/tree/master/Introspection/Launchalot – CommonsWare May 25 '15 at 21:21
  • Thank you so much. Could you please make this an answer and I'll accept it? – Jeremy Roberts May 25 '15 at 21:38
  • 6
    Have you tried using [`getLeanbackLaunchIntentForPackage()`](https://developer.android.com/reference/android/content/pm/PackageManager.html#getLeanbackLaunchIntentForPackage(java.lang.String))? – Sebastiano May 26 '15 at 08:46
  • 1
    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 issue – FrankKrumnow Sep 06 '18 at 14:59
  • This code worked for me, But I didn't describe Leanback launcher explicitly. Simply adding into the manifest file worked for me. – Meet Jan 07 '21 at 10:40

5 Answers5

67

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

Billda
  • 5,707
  • 2
  • 25
  • 44
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.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I tried your answer modified, here but it failed. Can you please suggest an answer? http://stackoverflow.com/questions/38856092/why-doesnt-ui-automator-launch-activity – likejudo Aug 09 '16 at 16:32
  • 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. – FrankKrumnow Sep 06 '18 at 14:59
  • 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. – Piotr Śmietana Feb 28 '19 at 14:48
  • 1
    @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 `` elements on the `` 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 `` 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. – CommonsWare Mar 03 '19 at 21:01
  • 2
    This is actually what `getLaunchIntentForPackage` does under the hood anyway: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/ApplicationPackageManager.java 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. – Adam Burley Dec 09 '21 at 17:49
24

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>
lunarzshine
  • 321
  • 2
  • 9
0

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" />
s-hunter
  • 24,172
  • 16
  • 88
  • 130
-3
    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
                }
Peter
  • 165
  • 1
  • 8