Resolving an Implicit Intent

NerdLauncher will show the user a list of launchable apps on the device. (A launchable app is an app the user can open by clicking an icon on the Home or launcher screen.) To do so, it will query the system (using the PackageManager) for launchable main activities. Launchable main activities are simply activities with intent filters that include a MAIN action and a LAUNCHER category. You have seen this intent filter in the AndroidManifest.xml file in your projects:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

In NerdLauncherFragment.java, add a method named setupAdapter() and call that method from onCreateView(…). (Ultimately this method will create a RecyclerView.Adapter instance and set it on your RecyclerView object. For now, it will just generate a list of application data.) Also, create an implicit intent and get a list of activities that match the intent from the PackageManager. Log the number of activities that the PackageManager returns.

Listing 24.3  Querying the PackageManager (NerdLauncherFragment.java)

public class NerdLauncherFragment extends Fragment {
    private static final String TAG = "NerdLauncherFragment";

    private RecyclerView mRecyclerView;

    public static NerdLauncherFragment newInstance() {
        return new NerdLauncherFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        ...
        setupAdapter();
        return v;
    }

    private void setupAdapter() {
        Intent startupIntent = new Intent(Intent.ACTION_MAIN);
        startupIntent.addCategory(Intent.CATEGORY_LAUNCHER);

        PackageManager pm = getActivity().getPackageManager();
        List<ResolveInfo> activities = pm.queryIntentActivities(startupIntent, 0);

        Log.i(TAG, "Found " + activities.size() + " activities.");
    }
}

Run NerdLauncher and check Logcat to see how many apps the PackageManager returned. (We got 30 the first time we tried it.)

In CriminalIntent, you used an implicit intent to send a crime report. You presented an activity chooser by creating an implicit intent, wrapping it in a chooser intent, and sending it to the OS with startActivity(Intent):

Intent i = new Intent(Intent.ACTION_SEND);
... // Create and put intent extras
i = Intent.createChooser(i, getString(R.string.send_report));
startActivity(i);

You may be wondering why you are not using that approach here. The short explanation is that the MAIN/LAUNCHER intent filter may or may not match a MAIN/LAUNCHER implicit intent that is sent via startActivity(Intent).

startActivity(Intent) does not mean, Start an activity matching this implicit intent. It means, Start the default activity matching this implicit intent. When you send an implicit intent via startActivity(Intent) (or startActivityForResult(…)), the OS secretly adds the Intent.CATEGORY_DEFAULT category to the intent.

Thus, if you want an intent filter to match implicit intents sent via startActivity(Intent), you must include the DEFAULT category in that intent filter.

An activity that has the MAIN/LAUNCHER intent filter is the main entry point for the app that it belongs to. It only wants the job of main entry point for that application. It typically does not care about being the default main entry point, so it does not have to include the CATEGORY_DEFAULT category.

Because MAIN/LAUNCHER intent filters may not include CATEGORY_DEFAULT, you cannot reliably match them to an implicit intent sent via startActivity(Intent). Instead, you use the intent to query the PackageManager directly for activities with the MAIN/LAUNCHER intent filter.

The next step is to display the labels of these activities in NerdLauncherFragment’s RecyclerView. An activity’s label is its display name – something the user should recognize. Given that these activities are launcher activities, the label is most likely the application name.

You can find the labels for the activities, along with other metadata, in the ResolveInfo objects that the PackageManager returned.

First, sort the ResolveInfo objects returned from the PackageManager alphabetically by label using the ResolveInfo.loadLabel(PackageManager) method.

Listing 24.4  Sorting alphabetically (NerdLauncherFragment.java)

public class NerdLauncherFragment extends Fragment {
    ...
    private void setupAdapter() {
        ...
        List<ResolveInfo> activities = pm.queryIntentActivities(startupIntent, 0);
        Collections.sort(activities, new Comparator<ResolveInfo>() {
            public int compare(ResolveInfo a, ResolveInfo b) {
                PackageManager pm = getActivity().getPackageManager();
                return String.CASE_INSENSITIVE_ORDER.compare(
                        a.loadLabel(pm).toString(),
                        b.loadLabel(pm).toString());
            }
        });
        Log.i(TAG, "Found " + activities.size() + " activities.");
    }
}

Now define a ViewHolder that displays an activity’s label. Store the activity’s ResolveInfo in a member variable (you will use it more than once later on).

Listing 24.5  ViewHolder implementation (NerdLauncherFragment.java)

public class NerdLauncherFragment extends Fragment {
    ...
    private void setupAdapter() {
        ...
    }

    private class ActivityHolder extends RecyclerView.ViewHolder {
        private ResolveInfo mResolveInfo;
        private TextView mNameTextView;

        public ActivityHolder(View itemView) {
            super(itemView);
            mNameTextView = (TextView) itemView;
        }

        public void bindActivity(ResolveInfo resolveInfo) {
            mResolveInfo = resolveInfo;
            PackageManager pm = getActivity().getPackageManager();
            String appName = mResolveInfo.loadLabel(pm).toString();
            mNameTextView.setText(appName);
        }
    }
}

Next add a RecyclerView.Adapter implementation.

Listing 24.6  RecyclerView.Adapter implementation (NerdLauncherFragment.java)

public class NerdLauncherFragment extends Fragment {
    ...
    private class ActivityHolder extends RecyclerView.ViewHolder {
        ...
    }

    private class ActivityAdapter extends RecyclerView.Adapter<ActivityHolder> {
        private final List<ResolveInfo> mActivities;

        public ActivityAdapter(List<ResolveInfo> activities) {
            mActivities = activities;
        }

        @Override
        public ActivityHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
            View view = layoutInflater
                    .inflate(android.R.layout.simple_list_item_1, parent, false);
            return new ActivityHolder(view);
        }

        @Override
        public void onBindViewHolder(ActivityHolder holder, int position) {
            ResolveInfo resolveInfo = mActivities.get(position);
            holder.bindActivity(resolveInfo);
        }

        @Override
        public int getItemCount() {
            return mActivities.size();
        }
    }
}

Last, but not least, update setupAdapter() to create an instance of ActivityAdapter and set it as the RecyclerView’s adapter.

Listing 24.7  Setting RecyclerView’s adapter (NerdLauncherFragment.java)

public class NerdLauncherFragment extends Fragment {
    ...
    private void setupAdapter() {
        ...
        Log.i(TAG, "Found " + activities.size() + " activities.");
        mRecyclerView.setAdapter(new ActivityAdapter(activities));
    }
    ...
}

Run NerdLauncher, and you will see a RecyclerView populated with activity labels (Figure 24.4).

Figure 24.4  All your activities are belong to us

All your activities are belong to us
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.213.110.162