In this tutorial, I will make a completed app which will listing all installed apps in Android and provide some extra features: search, directly running,...to make a "shortcut" when launching any app in our own device.
Project source code now available on Github, you can check it. First, please see this DEMO VIDEO, app run with Genymotion emulator:
Listing all Apps
As note above, we will a PackageManager instance to get all installed app. In programmatically code for activity, this process declaration only 2 lines:
PackageManager packageManager = getPackageManager(); List<Applicationinfo> list = packageManager.getInstalledApplications(PackageManager.GET_META_DATA)For app running well, we should do this process in a background thread by an AsyncTask. So, with getInstalledApplications() method was called in doInbackground(), an ApplicationInfo list will be return in onPostExcute(). Full source code for this AsyncTask:
package devexchanges.info.appslist; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; import java.util.ArrayList; import java.util.List; public class GetAllAppsTask extends AsyncTask<Void, Void, List<ApplicationInfo>> { private MainActivity activity; private List<Applicationinfo> apps; private PackageManager packageManager; public GetAllAppsTask(MainActivity activity, List<Applicationinfo> apps, PackageManager pm) { this.activity = activity; this.apps = apps; this.packageManager = pm; } @Override protected List<Applicationinfo> doInBackground(Void... params) { apps = checkForLaunchIntent(packageManager.getInstalledApplications(PackageManager.GET_META_DATA)); return apps; } private List<Applicationinfo> checkForLaunchIntent(List<Applicationinfo> list) { ArrayList<Applicationinfo> applist = new ArrayList<>(); for (ApplicationInfo applicationInfo : list) { try { if (packageManager.getLaunchIntentForPackage(applicationInfo.packageName) != null) { applist.add(applicationInfo); } } catch (Exception ex) { ex.printStackTrace(); } } return applist; } @Override protected void onPostExecute(List<Applicationinfo> list) { super.onPostExecute(list); activity.callBackDataFromAsynctask(list); } }In onPostExcute() method, I've called back data to main activity and update views. So, this method in activity code is simple like this:
public void callBackDataFromAsynctask(List<Applicationinfo> list) { applicationInfos.clear(); for (int i = 0; i < list.size(); i++) { applicationInfos.add(list.get(i)); } headerText.setText("All Apps (" + applicationInfos.size() + ")"); adapter.notifyDataSetChanged(); progressDialog.dismiss(); //dismiss progressing dialog }In order to our app always update, invoke AsynTask in onStart() method:
@Override protected void onStart() { super.onStart(); //invoke asynctask new GetAllAppsTask(this, applicationInfos, packageManager).execute(); }
Create SearchView to filter List
First, make a Toolbar layout and include I was included it in activity_main.xml above: Create a menu in our activity (in res/menu folder):
Back to activity code, create menu in onCreateOptionsMenu() and provide OnQueryTextListener for SearchView, when we typing on it, app will filter ListView through call:
//filter adapter and update ListView adapter.getFilter().filter(s);Creating menu search methods:
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); // Inflate menu to add items to action bar if it is present. inflater.inflate(R.menu.menu_main, menu); // Associate searchable configuration with the SearchView SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); searchView.setOnQueryTextListener(onQueryTextListener()); // text changed listener searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); return true; } private SearchView.OnQueryTextListener onQueryTextListener() { return new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { return false; } @Override public boolean onQueryTextChange(String s) { //filter adapter and update ListView adapter.getFilter().filter(s); return false; } }; }
Customizing a ListView Adapter
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); // If holder not exist then locate all view from UI file. if (convertView == null) { // inflate UI from XML file convertView = inflater.inflate(R.layout.item_listview, parent, false); // get all UI view holder = new ViewHolder(convertView); // set tag for holder convertView.setTag(holder); } else { // if holder created, get tag from view holder = (ViewHolder) convertView.getTag(); } //setting data to views holder.appName.setText(getItem(position).loadLabel(packageManager)); //get app name holder.appPackage.setText(getItem(position).packageName); //get app package holder.icon.setImageDrawable(getItem(position).loadIcon(packageManager)); //get app icon //set on click event for each item view convertView.setOnClickListener(onClickListener(position)); return convertView; } private class ViewHolder { private ImageView icon; private TextView appName; private TextView appPackage; public ViewHolder(View v) { icon = (ImageView) v.findViewById(R.id.icon); appName = (TextView) v.findViewById(R.id.name); appPackage = (TextView) v.findViewById(R.id.app_package); } }When clicking at each item, getting this item (app) packagename through getLaunchIntentForPackage() and lauched it by Intent:
private View.OnClickListener onClickListener(final int position) { return new View.OnClickListener() { @Override public void onClick(View v) { ApplicationInfo app = appsList.get(position); try { Intent intent = packageManager.getLaunchIntentForPackage(app.packageName); activity.startActivity(intent); if (null != intent) { activity.startActivity(intent); } } catch (ActivityNotFoundException e) { e.printStackTrace(); } } }; }Create customize Filter in our adapter to filtering data (ApplicationInfos List). We will override performFiltering() and publishResult() methods:
private class AppsFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); if (constraint != null && constraint.length() > 0) { ArrayList<Applicationinfo> filterList = new ArrayList<Applicationinfo>(); for (int i = 0; i < originalList.size(); i++) { if ((originalList.get(i).loadLabel(packageManager).toString().toUpperCase()) .contains(constraint.toString().toUpperCase())) { ApplicationInfo applicationInfo = originalList.get(i); filterList.add(applicationInfo); } } results.count = filterList.size(); results.values = filterList; } else { results.count = originalList.size(); results.values = originalList; } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { appsList = (ArrayList<Applicationinfo>) results.values; notifyDataSetChanged(); if (appsList.size() == originalList.size()) { activity.updateUILayout("All apps (" + appsList.size() + ")"); } else { activity.updateUILayout("Filtered apps (" + appsList.size() + ")"); } } }
Final code
Code for main activity:
package devexchanges.info.appslist; import android.app.ProgressDialog; import android.app.SearchManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private ListView listView; private TextView headerText; private Toolbar toolbar; private PackageManager packageManager; private ArrayAdapter<Applicationinfo> adapter; private ArrayList<Applicationinfo> applicationInfos; private ProgressDialog progressDialog; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); packageManager = getPackageManager(); applicationInfos = new ArrayList<>(); //find View by id listView = (ListView) findViewById(R.id.list_view); toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); //show progress dialog progressDialog = ProgressDialog.show(this, "Loading All Apps", "Loading application info..."); //set list view adapter LayoutInflater inflater = getLayoutInflater(); View header = inflater.inflate(R.layout.layout_lv_header, listView, false); headerText = (TextView) header.findViewById(R.id.text_header); listView.addHeaderView(header, null, false); //initializing and set adapter for listview adapter = new ApplicationAdapter(this, R.layout.item_listview, applicationInfos); listView.setAdapter(adapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); // Inflate menu to add items to action bar if it is present. inflater.inflate(R.menu.menu_main, menu); // Associate searchable configuration with the SearchView SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); searchView.setOnQueryTextListener(onQueryTextListener()); // text changed listener searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); return true; } private SearchView.OnQueryTextListener onQueryTextListener() { return new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { return false; } @Override public boolean onQueryTextChange(String s) { //filter adapter and update ListView adapter.getFilter().filter(s); return false; } }; } @Override protected void onStart() { super.onStart(); //invoke asynctask new GetAllAppsTask(this, applicationInfos, packageManager).execute(); } public void callBackDataFromAsynctask(List<Applicationinfo> list) { applicationInfos.clear(); for (int i = 0; i < list.size(); i++) { applicationInfos.add(list.get(i)); } headerText.setText("All Apps (" + applicationInfos.size() + ")"); adapter.notifyDataSetChanged(); progressDialog.dismiss(); } public void updateUILayout(String content) { headerText.setText(content); } }ListView adapter class, defining a customize Filter:
package devexchanges.info.appslist; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Filter; import android.widget.ImageView; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class ApplicationAdapter extends ArrayAdapter<Applicationinfo> { private List<Applicationinfo> appsList; private List<Applicationinfo> originalList; private MainActivity activity; private PackageManager packageManager; private AppsFilter filter; public ApplicationAdapter(MainActivity activity, int textViewResourceId, List<Applicationinfo> appsList) { super(activity, textViewResourceId, appsList); this.activity = activity; this.appsList = appsList; this.originalList = appsList; packageManager = activity.getPackageManager(); } @Override public int getCount() { return appsList.size(); } @Override public ApplicationInfo getItem(int position) { return appsList.get(position); } @Override public long getItemId(int position) { return appsList.indexOf(getItem(position)); } @Override public Filter getFilter() { if (filter == null) { filter = new AppsFilter(); } return filter; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); // If holder not exist then locate all view from UI file. if (convertView == null) { // inflate UI from XML file convertView = inflater.inflate(R.layout.item_listview, parent, false); // get all UI view holder = new ViewHolder(convertView); // set tag for holder convertView.setTag(holder); } else { // if holder created, get tag from view holder = (ViewHolder) convertView.getTag(); } //setting data to views holder.appName.setText(getItem(position).loadLabel(packageManager)); //get app name holder.appPackage.setText(getItem(position).packageName); //get app package holder.icon.setImageDrawable(getItem(position).loadIcon(packageManager)); //get app icon //set on click event for each item view convertView.setOnClickListener(onClickListener(position)); return convertView; } private View.OnClickListener onClickListener(final int position) { return new View.OnClickListener() { @Override public void onClick(View v) { ApplicationInfo app = appsList.get(position); try { Intent intent = packageManager.getLaunchIntentForPackage(app.packageName); activity.startActivity(intent); if (null != intent) { activity.startActivity(intent); } } catch (ActivityNotFoundException e) { e.printStackTrace(); } } }; } private class ViewHolder { private ImageView icon; private TextView appName; private TextView appPackage; public ViewHolder(View v) { icon = (ImageView) v.findViewById(R.id.icon); appName = (TextView) v.findViewById(R.id.name); appPackage = (TextView) v.findViewById(R.id.app_package); } } private class AppsFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); if (constraint != null && constraint.length() > 0) { ArrayList<Applicationinfo> filterList = new ArrayList<Applicationinfo>(); for (int i = 0; i < originalList.size(); i++) { if ((originalList.get(i).loadLabel(packageManager).toString().toUpperCase()) .contains(constraint.toString().toUpperCase())) { ApplicationInfo applicationInfo = originalList.get(i); filterList.add(applicationInfo); } } results.count = filterList.size(); results.values = filterList; } else { results.count = originalList.size(); results.values = originalList; } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { appsList = (ArrayList<Applicationinfo>) results.values; notifyDataSetChanged(); if (appsList.size() == originalList.size()) { activity.updateUILayout("All apps (" + appsList.size() + ")"); } else { activity.updateUILayout("Filtered apps (" + appsList.size() + ")"); } } } }Each ListView item layout:
Strings resource: Colors resource: Styling our app:
Conclusion
You can directly download app apk HERE!
Update: version 2.0: add feature: swipe to Uninstall an app, fix layout for tablet. Check this direct link for new apk file.