In Android the screen that appears when the phone starts is called “Launcher Screen”. It is possible in Android to write custom launcher apps which can be used as a replacement for the default launcher app that comes bundled with the phone. Developing a launcher app is no different than developing any other Android application, in fact both are same. In this post I’ll share what you need to do to write your own custom launcher application.
AndroidManifest.xml
I’ll skip the part of creating the project and drive straight to the code. Here is our sample AndroidManifest.xml file, remember to pay attention to the comments in the code.
<?xml version="1.0" encoding="utf-8"?><manifestxmlns:android="http://schemas.android.com/apk/res/android"package="ch.arnab.simplelauncher"android:versionCode="1"android:versionName="1.0"><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="16"/><applicationandroid:launchMode="singleTask"android:clearTaskOnLaunch="true"android:stateNotNeeded="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme"><activityandroid:name="ch.arnab.simplelauncher.HomeScreen"android:label="@string/app_name"android:launchMode="singleTask"android:excludeFromRecents="true"android:screenOrientation="nosensor"><intent-filter><actionandroid:name="android.intent.action.MAIN"/><!-- The following two intent-filters are the key to set homescreen --><categoryandroid:name="android.intent.category.HOME"/><categoryandroid:name="android.intent.category.DEFAULT"/></intent-filter></activity></application></manifest>
The important line in the above XML file is <category android:name="android.intent.category.HOME" />, this intent-filter allows you to set your application as Home Screen application. Android looks for this particular intent filter and whenever you install your app with this intent-filter set then your application will appear in the list of installed launchers (a tap on the Home button will reveal the list).
Display installed applications in our custom homescreen
Now as we have finished with the manifest file, let’s add some code to display the list of installed applications in our Home Screen, this way we can at least use the app after installing.
AsyncTaskLoader to asynchronously load applications
Here is the code to load the applications list asynchronously, we’re using a custom AsyncTaskLoader class, later we’ll hook it up in our fragment class using the Android Loaders.
publicclassAppsLoaderextendsAsyncTaskLoader<ArrayList<AppModel>>{ArrayList<AppModel>mInstalledApps;finalPackageManagermPm;publicAppsLoader(Contextcontext){super(context);mPm=context.getPackageManager();}@OverridepublicArrayList<AppModel>loadInBackground(){// retrieve the list of installed applicationsList<ApplicationInfo>apps=mPm.getInstalledApplications(0);if(apps==null){apps=newArrayList<ApplicationInfo>();}finalContextcontext=getContext();// create corresponding apps and load their labelsArrayList<AppModel>items=newArrayList<AppModel>(apps.size());for(inti=0;i<apps.size();i++){Stringpkg=apps.get(i).packageName;// only apps which are launchableif(context.getPackageManager().getLaunchIntentForPackage(pkg)!=null){AppModelapp=newAppModel(context,apps.get(i));app.loadLabel(context);items.add(app);}}// sort the listCollections.sort(items,ALPHA_COMPARATOR);returnitems;}@OverridepublicvoiddeliverResult(ArrayList<AppModel>apps){if(isReset()){// An async query came in while the loader is stopped. We// don't need the result.if(apps!=null){onReleaseResources(apps);}}ArrayList<AppModel>oldApps=apps;mInstalledApps=apps;if(isStarted()){// If the Loader is currently started, we can immediately// deliver its results.super.deliverResult(apps);}// At this point we can release the resources associated with// 'oldApps' if needed; now that the new result is delivered we// know that it is no longer in use.if(oldApps!=null){onReleaseResources(oldApps);}}@OverrideprotectedvoidonStartLoading(){if(mInstalledApps!=null){// If we currently have a result available, deliver it// immediately.deliverResult(mInstalledApps);}if(takeContentChanged()||mInstalledApps==null){// If the data has changed since the last time it was loaded// or is not currently available, start a load.forceLoad();}}@OverrideprotectedvoidonStopLoading(){// Attempt to cancel the current load task if possible.cancelLoad();}@OverridepublicvoidonCanceled(ArrayList<AppModel>apps){super.onCanceled(apps);// At this point we can release the resources associated with 'apps'// if needed.onReleaseResources(apps);}@OverrideprotectedvoidonReset(){// Ensure the loader is stoppedonStopLoading();// At this point we can release the resources associated with 'apps'// if needed.if(mInstalledApps!=null){onReleaseResources(mInstalledApps);mInstalledApps=null;}}/** * Helper method to do the cleanup work if needed, for example if we're * using Cursor, then we should be closing it here * * @param apps */protectedvoidonReleaseResources(ArrayList<AppModel>apps){// do nothing}/** * Perform alphabetical comparison of application entry objects. */publicstaticfinalComparator<AppModel>ALPHA_COMPARATOR=newComparator<AppModel>(){privatefinalCollatorsCollator=Collator.getInstance();@Overridepublicintcompare(AppModelobject1,AppModelobject2){returnsCollator.compare(object1.getLabel(),object2.getLabel());}};}
The loader class above will only retrieve the applications for which a “good” launch intent is available, put simply we’re only displaying those applications for which getLaunchIntentForPackage returns a valid launch intent.
GridView Adapter
A simple adapter used to populate the applications’ icons and names in a GridView.
publicclassAppListAdapterextendsArrayAdapter<AppModel>{privatefinalLayoutInflatermInflater;publicAppListAdapter(Contextcontext){super(context,android.R.layout.simple_list_item_2);mInflater=LayoutInflater.from(context);}publicvoidsetData(ArrayList<AppModel>data){clear();if(data!=null){addAll(data);}}@Override@TargetApi(Build.VERSION_CODES.HONEYCOMB)publicvoidaddAll(Collection<?extendsAppModel>items){//If the platform supports it, use addAll, otherwise add in loopif(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){super.addAll(items);}else{for(AppModelitem:items){super.add(item);}}}/** * Populate new items in the list. */@OverridepublicViewgetView(intposition,ViewconvertView,ViewGroupparent){Viewview;if(convertView==null){view=mInflater.inflate(R.layout.list_item_icon_text,parent,false);}else{view=convertView;}AppModelitem=getItem(position);((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());((TextView)view.findViewById(R.id.text)).setText(item.getLabel());returnview;}}
Grid Fragment
Grid view container fragment class, uses Android Loaders to load the list of applications and displays them in the Grid view.
publicclassAppsGridFragmentextendsGridFragmentimplementsLoaderManager.LoaderCallbacks<ArrayList<AppModel>>{AppListAdaptermAdapter;@OverridepublicvoidonActivityCreated(BundlesavedInstanceState){super.onActivityCreated(savedInstanceState);setEmptyText("No Applications");mAdapter=newAppListAdapter(getActivity());setGridAdapter(mAdapter);// till the data is loaded display a spinnersetGridShown(false);// create the loader to load the apps list in backgroundgetLoaderManager().initLoader(0,null,this);}@OverridepublicLoader<ArrayList<AppModel>>onCreateLoader(intid,Bundlebundle){returnnewAppsLoader(getActivity());}@OverridepublicvoidonLoadFinished(Loader<ArrayList<AppModel>>loader,ArrayList<AppModel>apps){mAdapter.setData(apps);if(isResumed()){setGridShown(true);}else{setGridShownNoAnimation(true);}}@OverridepublicvoidonLoaderReset(Loader<ArrayList<AppModel>>loader){mAdapter.setData(null);}@OverridepublicvoidonGridItemClick(GridViewg,Viewv,intposition,longid){AppModelapp=(AppModel)getGridAdapter().getItem(position);if(app!=null){Intentintent=getActivity().getPackageManager().getLaunchIntentForPackage(app.getApplicationPackageName());if(intent!=null){startActivity(intent);}}}}
As it’s a Launcher app, so when you install it you don’t get to see anything unless you tap on the home button. The tap on the Home button shows you a chooser dialog from which you can select the appropriate Launcher app.
That’s it, you now have your own custom launcher application. Although a full-fledged launcher app like the ones that come with Android phones has many features built into them, but you can use this as a basic building block and start writing a more advanced and complex launcher as you learn.
For those who wants to investigate further, do take a look at the default launcher application code here: Android Stock Launcher App
Update: I wrote another post which provides a high-level overview of what all things are required to develop a “kiosk-mode” Android application. You can check it out here – Developing kiosk-mode applications in Android
You can download the full source code used in this article from this Github repository.
Note: I have developed a generic Kiosk Management Solution called MobiLock Pro which has the above functionality as well as many other features. If you want to have a fully managed Cloud Based Kiosk solution then do check out MobiLock Pro.