Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
4/9/14 11:23 PM
Arnab Chakraborty
A cognitive perspective on web. Home Archives About
AndroidManifest.xml
Ill 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. AndroidManifest.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="ch.arnab.simplelauncher" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="8" 9 android:targetSdkVersion="16" /> 10 11 <application 12 android:launchMode="singleTask" 13 android:clearTaskOnLaunch="true" 14 android:stateNotNeeded="true" 15 android:icon="@drawable/ic_launcher" 16 android:label="@string/app_name" 17 android:theme="@style/AppTheme" > 18 <activity 19 android:name="ch.arnab.simplelauncher.HomeScreen" 20 android:label="@string/app_name" 21 android:launchMode="singleTask" 22 android:excludeFromRecents="true" 23 android:screenOrientation="nosensor"> 24 <intent-filter> 25 <action android:name="android.intent.action.MAIN" /> 26 <!-- The following two intent-filters are the key to set homescreen --> 27 <category android:name="android.intent.category.HOME" /> 28 <category android:name="android.intent.category.DEFAULT" /> 29 30 </intent-filter> 31 </activity> 32 </application> 33 </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).
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/
Page 1 of 13
4/9/14 11:23 PM
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/
Page 2 of 13
4/9/14 11:23 PM
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 }
} @Override protected void onStartLoading() { 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(); } } @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } @Override public void onCanceled(ArrayList<AppModel> apps) { super.onCanceled(apps); // At this point we can release the resources associated with 'apps' // if needed. onReleaseResources(apps); } @Override protected void onReset() { // Ensure the loader is stopped onStopLoading(); // 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 */ protected void onReleaseResources(ArrayList<AppModel> apps) { // do nothing } /** * Perform alphabetical comparison of application entry objects. */ public static final Comparator<AppModel> ALPHA_COMPARATOR = new Comparator<AppModel>() { private final Collator sCollator = Collator.getInstance(); @Override public int compare(AppModel object1, AppModel object2) { return sCollator.compare(object1.getLabel(), object2.getLabel()); } };
The loader class above will only retrieve the applications for which a good launch intent is available, put simply were 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. AppListAdapter.java
1 public class AppListAdapter extends ArrayAdapter<AppModel> {
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/ Page 3 of 13
4/9/14 11:23 PM
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 }
private final LayoutInflater mInflater; public AppListAdapter (Context context) { super(context, android.R.layout.simple_list_item_2); mInflater = LayoutInflater.from(context); } public void setData(ArrayList<AppModel> data) { clear(); if (data != null) { addAll(data); } } @Override @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void addAll(Collection<? extends AppModel> items) { //If the platform supports it, use addAll, otherwise add in loop if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { super.addAll(items); }else{ for(AppModel item: items){ super.add(item); } } } /** * Populate new items in the list. */ @Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView == null) { view = mInflater.inflate(R.layout.list_item_icon_text, parent, false); } else { view = convertView; } AppModel item = getItem(position); ((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon()); ((TextView)view.findViewById(R.id.text)).setText(item.getLabel()); return view; }
Grid Fragment
Grid view container fragment class, uses Android Loaders to load the list of applications and displays them in the Grid view. AppsGridFragment.java
1 public class AppsGridFragment extends GridFragment implements LoaderManager.LoaderCallbacks<ArrayList<AppModel>> { 2 3 AppListAdapter mAdapter; 4 5 @Override 6 public void onActivityCreated(Bundle savedInstanceState) { 7 super.onActivityCreated(savedInstanceState); 8 9 setEmptyText("No Applications"); 10 11 mAdapter = new AppListAdapter(getActivity()); 12 setGridAdapter(mAdapter); 13 14 // till the data is loaded display a spinner 15 setGridShown(false); 16 17 // create the loader to load the apps list in background 18 getLoaderManager().initLoader(0, null, this); 19 } 20 21 @Override 22 public Loader<ArrayList<AppModel>> onCreateLoader(int id, Bundle bundle) {
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/ Page 4 of 13
4/9/14 11:23 PM
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 }
return new AppsLoader(getActivity()); } @Override public void onLoadFinished(Loader<ArrayList<AppModel>> loader, ArrayList<AppModel> apps) { mAdapter.setData(apps); if (isResumed()) { setGridShown(true); } else { setGridShownNoAnimation(true); } } @Override public void onLoaderReset(Loader<ArrayList<AppModel>> loader) { mAdapter.setData(null); } @Override public void onGridItemClick(GridView g, View v, int position, long id) { AppModel app = (AppModel) getGridAdapter().getItem(position); if (app != null) { Intent intent = getActivity().getPackageManager().getLaunchIntentForPackage(app.getApplicationPackageName()); if (intent != null) { startActivity(intent); } } }
Layout file
A simple layout file to embed the grid fragment. homescreen.xml
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingLeft="@dimen/activity_horizontal_margin" 6 android:paddingRight="@dimen/activity_horizontal_margin" 7 android:paddingTop="@dimen/activity_vertical_margin" 8 android:paddingBottom="@dimen/activity_vertical_margin" 9 tools:context=".HomeScreen"> 10 11 <fragment 12 android:layout_width="match_parent" 13 android:layout_height="match_parent" 14 android:name="ch.arnab.simplelauncher.AppsGridFragment" 15 android:id="@+id/apps_grid" /> 16 17 </RelativeLayout>
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/
Page 5 of 13
4/9/14 11:23 PM
You can download the full source code used in this article from this Github repository. arnabc / simplelauncher
An example custom launcher for Android Read More http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/
Latest commit to the master branch on 3-16-2014 23 11
Download as zip
Comments
72 Comments
Sort by Best
arnab.ch
Share
Login
Favorite
I found an issue - after uninstalling an app, the UI won't refresh. Is there a way to auto-refresh or use a call to refresh the UI?
! Reply ! Share
webgyani
Mod
In your AppsGridFragment, register a BroadcastReceiver which should use an IntentFilter for ACTION_PACKAGE_ADDED and ACTION_PACKAGE_REMOVED intent actions. Then on the onReceive method of that receiver call getLoaderManager().getLoader(YOUR_LOADER_ID).onContentChanged(), this should ideally refresh the apps list. If you are using the above code then the LOADER_ID is 0 which was specified in the initLoader call.
1
! Reply ! Share
I tried - and got stuck, can you advice? On AndroidManifest.xml: <receiver android:name="virtualgs.ihome.ReceiveBroadcast"> <intent-filter> <action android:name="ACTION_PACKAGE_ADDED"/> <action android:name="ACTION_PACKAGE_REMOVED"/> </intent-filter> </receiver> and here's where I got issue: public class ReceiveBroadcast extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { getLoaderManager().getLoader(0).onContentChanged(); // Cannot do getLoaderManager } }
! Reply ! Share
Also tried: getLoaderManager is undefined. Please help! public class ReceiveBroadcast extends BroadcastReceiver implements LoaderManager.LoaderCallbacks<arraylist<appmodel>> { public void onReceive(Context context, Intent intent) { getLoaderManager().getLoader(0).onContentChanged(); // also got issue ...
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/ Page 6 of 13
4/9/14 11:23 PM
}
! Reply ! Share
Webgyani
Use an inner class and dynamically register and unregister the broadcast receivers from the fragment. Don't use receiver declaration in Manifest. See "registerReceiver" method doc for example.
! Reply ! Share
I also tried register/unregister the broadcast receiver in the Activity itself, also does not work. However, manual update with menu (getLoaderManager().getLoader(0).onContentChanged()) works, only cannot receiving the broadcast. public void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); registerReceiver(receiver, filter); } public void onPause() { super.onPause(); unregisterReceiver(receiver); }
! Reply ! Share
webgyani
Mod
The way you've set it up, this will certainly not work. You are registering the receiver in Activity "onResume" and unregistering the receiver on Activity "onPause", the moment you go to uninstall any app, I guess you do it by going through settings or other app which opens a new screen and when that happens "onPause" gets called hence the receiver gets unregistered and does not receive the intent broadcast. The simple solution could be to register/unregister the receiver in Activity onCreate/onDestroy methods. An alternate solution is that you can define a receiver in Manifest (like you did earlier) and receive the broadcast there and set some Preference flag when an app is deleted/added, and check that flag in your Activity's onStart/onResume callback to determine what to do, it should not be too difficult.
! Reply ! Share
It is not difficult, but I found that onReceive is not called at all. Do a simple log and nothing is shown in log. Looks so close to the result but still stuck. <receiver android:name=".Receiver"> <intent-filter> <action android:name="android.intent.action.PACKAGE_REMOVED"/> <action android:name="android.intent.action.PACKAGE_ADDED"/> </intent-filter> </receiver> public class Receiver extends BroadcastReceiver { public void onReceive(Context ctx, Intent intent) { Log.d("iHome", "received action!"); } }
! Reply ! Share
webgyani
Mod
I'll look into it, possibly sometime this week, I am bit busy in the first half of the week, but once I fix that I'll update this thread. Just give me a few days.
! Reply ! Share
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/
Page 7 of 13
4/9/14 11:23 PM
Good news! I found my answer here (need to add in <data android:scheme="package"/>), and coupled with your suggestion of using preference, works like a charm.
! Reply ! Share
webgyani
Mod
Great! :-)
! Reply ! Share
But that solution has an issue - apps are downloaded on background, if the user is using the launcher, refresh will not occurred (since the check only happened onResume()). I was wondering whether it is possible for the Broadcast Receiver to call the launcher and do the update.
! Reply ! Share
webgyani
Mod
Take a look at the latest code (pushed to GitHub), that issue has been fixed. I've also updated the Gradle build file, so you may have to update support library (19.0.1), Android SDK (19) and Gradle (0.7+) if you do not have the mentioned version of those libraries.
! Reply ! Share
Hi, since I have already deeply worked on my own solution (and I don't use Android Studio), can you tell me which file/code files has changed and I can check the solutions by studying the source code? And how do you solve it? Thanks!
! Reply ! Share
webgyani
Mod
Take a look at the commit details here: https://github.com/arnabc/simp... has been modified and PackageIntentReceiver.java has been newly introduced to handle install/uninstall intents.
AppsLoader.java
! Reply ! Share
Works! Thanks!
! Reply ! Share
webgyani
Mod
I'll look into it this weekend. I really appreciate the amount of effort that you've put into finding a solution, and I feel that the launcher should handle this scenario, just give me few days.
! Reply ! Share
Thanks! I have implemented every suggestions you gave, and looking forward for the solution! And if I have found a solution, I will update here too.
! Reply ! Share
Tried, in activity onCreate() receiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { ((AppsGridFragment) getSupportFragmentManager().findFragmentById(R.id.apps_grid)).refresh(); } };
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/ Page 8 of 13
4/9/14 11:23 PM
IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); registerReceiver(receiver, filter); Then onDestroy I unregister the receiver. Yes, then I go settings and uninstall and app, tried both Back or Home to go back to the installer... and nothing happened. Think Manifest became the last solution...
! Reply ! Share
Thanks, this is what I do but does not seem to work - In AppsGridFragment onActivityCreated method, I added the following code as advice: but go to settings and remove an app does not have any effect. -IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); BroadcastReceiver receiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { getLoaderManager().getLoader(0).onContentChanged(); }}; getActivity().registerReceiver(receiver, filter);
! Reply ! Share
Hi, I was wondering whether you can refresh your code with these changes. This will make a much more useful Launcher.
! Reply ! Share
! a month ago
Ask a question: as I am trying to design a Home screen for kids, I hope to do bigger icon that just 48dp - is it possible to get larger app icon? Currently loadIcon seems to returned me low resolution icons which look bad even at 72dp.
! Reply ! Share
webgyani
Mod
You can try to use getDrwableForDensity to return appropriate icon from the application's resources. A quick Google search revealed the following two links which should be of help, but to use those solutions you need to slightly adjust the code. http://stackoverflow.com/quest... http://stackoverflow.com/quest...
! Reply ! Share
For 1st approach, how to get the component name in AppModel? (ActivityInfo info = packageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA), for 2nd approach, seems to be API 17 only for the config.densityDpi, without this it won't work. So far I have been quite successful following your tutorial, thanks a lot!
1
! Reply ! Share
4/9/14 11:23 PM
limtc
! a month ago
Thanks for the tutorial. I try to write a customized kiosk where only a few apps are allowed (and home button will return to the app), it works fine except that some external apps will quit halfway if not launched from the default installer. For example, I can launch the Google gallery app, works fine until I select a picture, it will return to my installer! Is it something I didn't handle? Thanks!
! Reply ! Share
webgyani
Mod
Not exactly sure what could be the problem, but my guess would be to check the activity intent flags that you're passing when you invoke that Gallery app.
! Reply ! Share
Thanks. I am not really sure what happened, and hope to test your installer, but right now I don't know how to get it up and running as I am using Eclipse. I was wondering whether it is possible for you to kindly package the installer for use with Eclipse - so we can see it and running and learn your code on the way. Appreciated!
! Reply ! Share
Anyway, I got it working already with Eclipse. Steps to do: 1) Create a new project and make sure the package name is ch.arnab.simplelauncher, 2) Close the project and copy /res, /src and AndroidManifest.xml over, 3) Open the project.
! Reply ! Share
Felipe
! a month ago
Hi man, How i can set this laucher , how as a default laucher , when i open my cell open this laucher
! Reply ! Share
webgyani
Mod
After you install the app, press Home button which should show a dialog box with the list of Launcher applications, if you are using Phones running ICS or above then select the SimpleLauncher icon and then choose "Always" button, that makes it the default launcher and it survives the reboot as well. But remember this is a sample application and it does not have many features that other Launchers provide, you should only use it for experimental purpose and to learn how to write Launcher apps for Android.
! Reply ! Share
Felipe
Thanks Man!
Diego
! 3 months ago
tks.
! Reply ! Share
Paul Marcellana
! 3 months ago
Whoa! This is the one that I've looking for developing kiosk! But can't see and run the app. :'(
! Reply ! Share
Paul Marcellana
I can't even understand the code and there's sooooooo many errors that I couldn't resolve! :(
! Reply ! Share
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/
Page 10 of 13
4/9/14 11:23 PM
webgyani
Mod
What are the errors, by the way this is Gradle based project and it will only work in AndroidStudio, I didn't check it in Eclipse. Also I don't know what is causing you to not understand the code. Care to explain ?
! Reply ! Share
Paul Marcellana
I fixed the errors. but it doesn't run. :( I am new to android programming that's why I am getting hard time in understanding the codes.
! Reply ! Share
webgyani
Mod
Fair enough, to understand the code you need to know about Android Loaders, Fragments, ListAdapters and little bit of intent-filters. This is not exactly a beginner thing, but if you spend some time on it, then you should be able to understand it's not that complicated.
! Reply ! Share
Vinay B
! 3 months ago
Hi Arnab, I'm new to android app development. After downloading from github i am unable to run that application in my AVD. I had downloaded and using eclipse.exe of "adt-bundle-windows-x86_64-20131030" directory. Please help out by suggesting me step that should i follow and help to analyse the flow. Thanks in advance :)
! Reply ! Share
webgyani
Mod
That project is Gradle based, so you need to use AndroidStudio instead of Eclipse.
! Reply ! Share
Vinay B
webgyani
Mod
Why not use AndroidStudio, as far as I know Gradle support in ADT has lot of issues, or alternatively create your project in Eclipse and copy over the necessary files, change the package structure as you see fit.
! Reply ! Share
Per
! 3 months ago
Thanks for this tutorial. Do you know how to extends this to also include a custom lock screen which replaces the existing one? thanks in advance.
! Reply ! Share
webgyani
Mod
A lot of folks are asking this, being very honest I am yet to venture into the lock screen part, but probably I'll do it in some time, but cannot put a time on it.
! Reply ! Share
Tiger79
! 3 months ago
Hello Arnab, great article ! I was wondering though, what is your opinion regarding making a Launcher App if I want to develop a custom Lock Screen similar to the Galaxy Nexus lock screen. The one you need to move an icon over another icon to actually perform some action. Would implementing that into a Launcher app be the right choice ?
! Reply ! Share
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/
Page 11 of 13
4/9/14 11:23 PM
webgyani
Mod
You can implement it, although being very honest, I don't have any opinion whether it would be a right choice or not. There are some custom lock screen apps on Play Store and I guess not all of them are launcher apps.
! Reply ! Share
Rohit
! 4 months ago
Hi Arnab!!! Thanks for this amazing post. I am facing a problem. When i installed my launcher on my device, it shows only a loading circle. I am not able to detect my mistake. Please help!!!!
! Reply ! Share
webgyani
Mod
If you are using the above code, then check your logs, the loader is shown when we try to load the data using AsyncTaskLoader, may be that piece of code is throwing exception somewhere.
! Reply ! Share
Ankur Dogra
! 4 months ago
Thanx alot....!!!
! Reply ! Share
Lee Wright
! 4 months ago
Congrats guys! well done! This is huge for editors who need to do a lot of moderation!
! Reply ! Share
ALSO ON ARNAB.CH
WHAT'S THIS?
webgyani Let's take this discussion offline, you can get my email
knew that I wouldn't have to write own library :( Greate article, thanks!
from the about page, shoot me an email with your questions and I'll try to answer them as
Subscribe
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/
Page 12 of 13
4/9/14 11:23 PM
http://arnab.ch/blog/2013/08/how-to-write-custom-launcher-app-in-android/
Page 13 of 13