Android has such a great opportunity to be installed some kind of 3rd party launcher. There are a lot of launchers I can find that can be downloaded, but I can't get what I want. Some have a huge memories which are not good for my device and also make my device runs to be slowing down, and some have a beautiful appearance, but still hard and slow. I've been using Dodol Launcher for a long time as it is light, has a nice design, and simple, but sometimes it freezes up and makes my phone doesn't respond to anything.
The point is, I want something that is fit in with my own. So I thought, why not I make my own launcher?
I've searched for some tutorials about how to make my own Android launcher. It was a bit difficult as almost every sample projects that I got won't work and couldn't be installed even there was not such an error before finally I found this tutorial. It's pretty awesome and I start off by this tutorial although there are some errors in it. I use almost all of the code from that tutorial with some modifications so that it can be runned.
First thing of all we need to make a new Android Project, I'm using Eclipse. Make sure to uncheck "Create custom launcher icon" and "Create activity".
And make a new package inside 'src' folder and give it a name, ex. "com.example.mylauncher".
1. GridActivity class
Create a new class inside com.example.mylauncher package and give it a name GridActivity.java. Then type this code:
2. gridview.xml
Next, we make an .xml file inside res/layout folder and name it gridview. Then write this code:
4. AppsGridFragment class
Make a new class inside com.example.mylauncher package and name it AppsGridFragment.java then type this code:
5. GridFragment class
Next, make a new class inside our package named GridFragment.java. Type this code:
6. AppModel class
Next, make a new class inside our package named AppModel.java. And type this code:
7. AppListAdapter class
Next make a class named AppListAdapter.java and type this code:
8. AppsLoader class
Next make a new class inside our package named AppsLoader.java and type this code:
9. PackageIntentReceiver class
Next make a new class inside our package named PackageIntentReceiver.java and type this code:
10. list_item_icon_text.xml
Next, make a new .xml file named list_item_icon_text.xml inside res/layout folder. And type this code:
11. strings.xml
Edit strings.xml inside res/values folder with this code:
12. AndroidManifest.xml
Finally, adit/or and some code in AndroidManifest.xml file with like this:
That's it. After all is done, right click on the project and choose "run as -> Android Application".
This is the screenshot of my result:
I hope this is useful. Beside, this launcher result is a very simple. But we can expand that project.
Bye...
/bye
The point is, I want something that is fit in with my own. So I thought, why not I make my own launcher?
I've searched for some tutorials about how to make my own Android launcher. It was a bit difficult as almost every sample projects that I got won't work and couldn't be installed even there was not such an error before finally I found this tutorial. It's pretty awesome and I start off by this tutorial although there are some errors in it. I use almost all of the code from that tutorial with some modifications so that it can be runned.
First thing of all we need to make a new Android Project, I'm using Eclipse. Make sure to uncheck "Create custom launcher icon" and "Create activity".
And make a new package inside 'src' folder and give it a name, ex. "com.example.mylauncher".
1. GridActivity class
Create a new class inside com.example.mylauncher package and give it a name GridActivity.java. Then type this code:
package com.example.mylauncher; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.Menu; import android.view.Window; import android.view.WindowManager; public class GridActivity extends FragmentActivity{ @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); //requesting to turn the title off requestWindowFeature(Window.FEATURE_NO_TITLE); //making it full screen getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.gridview); } @Override public boolean onCreateOptionsMenu(Menu menu) { // TODO Auto-generated method stub getMenuInflater().inflate(R.menu.home_screen, menu); return true; } @Override public void onBackPressed() { // TODO Auto-generated method stub super.onBackPressed(); } }
2. gridview.xml
Next, we make an .xml file inside res/layout folder and name it gridview. Then write this code:
3. home_screen.xml
Make a new .xml file inside res/menu folder (create a new menu folder if it doesn't exist yet) and give it a name home_screen, then type this code:
package com.example.mylauncher; import java.util.ArrayList; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; import android.view.View; import android.widget.GridView; public class AppsGridFragment extends GridFragment implements LoaderCallbacks>{ AppListAdapter mAdapter; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setEmptyText("No Applications"); mAdapter = new AppListAdapter(getActivity()); setGridAdapter(mAdapter); // till the data is loaded display a spinner setGridShown(false); // create the loader to load the apps list in background getLoaderManager().initLoader(0, null, this); } @Override public Loader > onCreateLoader(int id, Bundle bundle) { return new AppsLoader(getActivity()); } @Override public void onLoadFinished(Loader > loader, ArrayList apps) { mAdapter.setData(apps); if (isResumed()) { setGridShown(true); } else { setGridShownNoAnimation(true); } } @Override public void onLoaderReset(Loader > 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); } } } }
package com.example.mylauncher; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.GridView; import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; public class GridFragment extends Fragment{ static final int INTERNAL_EMPTY_ID = 0x00ff0001; static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002; static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003; final private Handler mHandler = new Handler(); final private Runnable mRequestFocus = new Runnable() { public void run() { mGrid.focusableViewAvailable(mGrid); } }; final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { onGridItemClick((GridView) parent, v, position, id); } }; ListAdapter mAdapter; GridView mGrid; View mEmptyView; TextView mStandardEmptyView; View mProgressContainer; View mGridContainer; CharSequence mEmptyText; boolean mGridShown; public GridFragment() { } /** * Provide default implementation to return a simple grid view. Subclasses * can override to replace with their own layout. If doing so, the * returned view hierarchy must have a GridView whose id * is {@link android.R.id#list android.R.id.list} and can optionally * have a sibling view id {@link android.R.id#empty android.R.id.empty} * that is to be shown when the grid is empty. * * If you are overriding this method with your own custom content, * consider including the standard layout {@link android.R.layout#list_content} * in your layout file, so that you continue to retain all of the standard * behavior of ListFragment. In particular, this is currently the only * way to have the built-in indeterminant progress state be shown. */ @SuppressLint("NewApi") @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final Context context = getActivity(); FrameLayout root = new FrameLayout(context); // ------------------------------------------------------------------ LinearLayout pframe = new LinearLayout(context); pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); pframe.setOrientation(LinearLayout.VERTICAL); pframe.setVisibility(View.GONE); pframe.setGravity(Gravity.CENTER); ProgressBar progress = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge); pframe.addView(progress, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); root.addView(pframe, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); // ------------------------------------------------------------------ FrameLayout lframe = new FrameLayout(context); lframe.setId(INTERNAL_LIST_CONTAINER_ID); TextView tv = new TextView(getActivity()); tv.setId(INTERNAL_EMPTY_ID); tv.setGravity(Gravity.CENTER); lframe.addView(tv, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); GridView lv = new GridView(getActivity()); lv.setId(android.R.id.list); lv.setDrawSelectorOnTop(false); lv.setColumnWidth(convertDpToPixels(60, getActivity())); lv.setStretchMode(GridView.STRETCH_COLUMN_WIDTH); lv.setNumColumns(GridView.AUTO_FIT); lv.setHorizontalSpacing(convertDpToPixels(20, getActivity())); lv.setVerticalSpacing(convertDpToPixels(20, getActivity())); lv.setSmoothScrollbarEnabled(true); // disable overscroll if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { lv.setOverScrollMode(ListView.OVER_SCROLL_NEVER); } lframe.addView(lv, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); root.addView(lframe, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); // ------------------------------------------------------------------ root.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); return root; } /** * Attach to grid view once the view hierarchy has been created. */ @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); ensureGrid(); } /** * Detach from {@link GridView} */ @Override public void onDestroyView() { mHandler.removeCallbacks(mRequestFocus); mGrid = null; mGridShown = false; mEmptyView = mProgressContainer = mGridContainer = null; mStandardEmptyView = null; super.onDestroyView(); } public static int convertDpToPixels(float dp, Context context){ Resources resources = context.getResources(); return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, resources.getDisplayMetrics() ); } /** * This method will be called when an item in the grid is selected. * Subclasses should override. Subclasses can call * getGridView().getItemAtPosition(position) if they need to access the * data associated with the selected item. * * @param g The {@link GridView} where the click happened * @param v The view that was clicked within the {@link GridView} * @param position The position of the view in the grid * @param id The row id of the item that was clicked */ public void onGridItemClick(GridView g, View v, int position, long id) { } /** * Provide the cursor for the {@link GridView}. */ public void setGridAdapter(ListAdapter adapter) { final boolean hadAdapter = (mAdapter != null); mAdapter = adapter; if (mGrid != null) { mGrid.setAdapter(adapter); if (!mGridShown && !hadAdapter) { // The grid was hidden, and previously didn't have an // adapter. It is now time to show it. setGridShown(true, (getView().getWindowToken() != null)); } } } /** * Set the currently selected grid item to the specified * position with the adapter's data * * @param position */ public void setSelection(int position) { ensureGrid(); mGrid.setSelection(position); } /** * Get the position of the currently selected grid item. */ public int getSelectedItemPosition() { ensureGrid(); return mGrid.getSelectedItemPosition(); } /** * Get the cursor row ID of the currently selected grid item. */ public long getSelectedItemId() { ensureGrid(); return mGrid.getSelectedItemId(); } /** * Get the activity's {@link GridView} widget. */ public GridView getGridView() { ensureGrid(); return mGrid; } /** * The default content for a ListFragment has a TextView that can * be shown when the grid is empty. If you would like to have it * shown, call this method to supply the text it should use. */ public void setEmptyText(CharSequence text) { ensureGrid(); if (mStandardEmptyView == null) { throw new IllegalStateException("Can't be used with a custom content view"); } mStandardEmptyView.setText(text); if (mEmptyText == null) { mGrid.setEmptyView(mStandardEmptyView); } mEmptyText = text; } /** * Control whether the grid is being displayed. You can make it not * displayed if you are waiting for the initial data to show in it. During * this time an indeterminant progress indicator will be shown instead. * *
Applications do not normally need to use this themselves. The default * behavior of ListFragment is to start with the grid not being shown, only * showing it once an adapter is given with {@link #setGridAdapter(ListAdapter)}. * If the grid at that point had not been shown, when it does get shown * it will be do without the user ever seeing the hidden state. * * @param shown If true, the grid view is shown; if false, the progress * indicator. The initial value is true. */ public void setGridShown(boolean shown) { setGridShown(shown, true); } /** * Like {@link #setGridShown(boolean)}, but no animation is used when * transitioning from the previous state. */ public void setGridShownNoAnimation(boolean shown) { setGridShown(shown, false); } /** * Control whether the grid is being displayed. You can make it not * displayed if you are waiting for the initial data to show in it. During * this time an indeterminant progress indicator will be shown instead. * * @param shown If true, the grid view is shown; if false, the progress * indicator. The initial value is true. * @param animate If true, an animation will be used to transition to the * new state. */ private void setGridShown(boolean shown, boolean animate) { ensureGrid(); if (mProgressContainer == null) { throw new IllegalStateException("Can't be used with a custom content view"); } if (mGridShown == shown) { return; } mGridShown = shown; if (shown) { if (animate) { mProgressContainer.startAnimation(AnimationUtils.loadAnimation( getActivity(), android.R.anim.fade_out)); mGridContainer.startAnimation(AnimationUtils.loadAnimation( getActivity(), android.R.anim.fade_in)); } else { mProgressContainer.clearAnimation(); mGridContainer.clearAnimation(); } mProgressContainer.setVisibility(View.GONE); mGridContainer.setVisibility(View.VISIBLE); } else { if (animate) { mProgressContainer.startAnimation(AnimationUtils.loadAnimation( getActivity(), android.R.anim.fade_in)); mGridContainer.startAnimation(AnimationUtils.loadAnimation( getActivity(), android.R.anim.fade_out)); } else { mProgressContainer.clearAnimation(); mGridContainer.clearAnimation(); } mProgressContainer.setVisibility(View.VISIBLE); mGridContainer.setVisibility(View.GONE); } } /** * Get the ListAdapter associated with this activity's {@link GridView}. */ public ListAdapter getGridAdapter() { return mAdapter; } private void ensureGrid() { if (mGrid != null) { return; } View root = getView(); if (root == null) { throw new IllegalStateException("Content view not yet created"); } if (root instanceof GridView) { mGrid = (GridView) root; } else { mStandardEmptyView = (TextView)root.findViewById(INTERNAL_EMPTY_ID); if (mStandardEmptyView == null) { mEmptyView = root.findViewById(android.R.id.empty); } else { mStandardEmptyView.setVisibility(View.GONE); } mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID); mGridContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID); View rawGridView = root.findViewById(android.R.id.list); if (!(rawGridView instanceof GridView)) { if (rawGridView == null) { throw new RuntimeException( "Your content must have a GridView whose id attribute is " + "'android.R.id.list'"); } throw new RuntimeException( "Content has view with id attribute 'android.R.id.list' " + "that is not a GridView class"); } mGrid = (GridView) rawGridView; if (mEmptyView != null) { mGrid.setEmptyView(mEmptyView); } else if (mEmptyText != null) { mStandardEmptyView.setText(mEmptyText); mGrid.setEmptyView(mStandardEmptyView); } } mGridShown = true; mGrid.setOnItemClickListener(mOnClickListener); if (mAdapter != null) { ListAdapter adapter = mAdapter; mAdapter = null; setGridAdapter(adapter); } else { // We are starting without an adapter, so assume we won't // have our data right away and start with the progress indicator. if (mProgressContainer != null) { setGridShown(false, false); } } mHandler.post(mRequestFocus); } }
6. AppModel class
Next, make a new class inside our package named AppModel.java. And type this code:
package com.example.mylauncher; import java.io.File; import android.content.Context; import android.content.pm.ApplicationInfo; import android.graphics.drawable.Drawable; public class AppModel { private final Context mContext; private final ApplicationInfo mInfo; private String mAppLabel; private Drawable mIcon; private boolean mMounted; private final File mApkFile; public AppModel(Context context, ApplicationInfo info) { mContext = context; mInfo = info; mApkFile = new File(info.sourceDir); } public ApplicationInfo getAppInfo() { return mInfo; } public String getApplicationPackageName() { return getAppInfo().packageName; } public String getLabel() { return mAppLabel; } public Drawable getIcon() { if (mIcon == null) { if (mApkFile.exists()) { mIcon = mInfo.loadIcon(mContext.getPackageManager()); return mIcon; } else { mMounted = false; } } else if (!mMounted) { // If the app wasn't mounted but is now mounted, reload // its icon. if (mApkFile.exists()) { mMounted = true; mIcon = mInfo.loadIcon(mContext.getPackageManager()); return mIcon; } } else { return mIcon; } return mContext.getResources().getDrawable(android.R.drawable.sym_def_app_icon); } void loadLabel(Context context) { if (mAppLabel == null || !mMounted) { if (!mApkFile.exists()) { mMounted = false; mAppLabel = mInfo.packageName; } else { mMounted = true; CharSequence label = mInfo.loadLabel(context.getPackageManager()); mAppLabel = label != null ? label.toString() : mInfo.packageName; } } } }
7. AppListAdapter class
Next make a class named AppListAdapter.java and type this code:
package com.example.mylauncher; import java.util.ArrayList; import java.util.Collection; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; public class AppListAdapter extends ArrayAdapter{ 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 data) { clear(); if (data != null) { addAll(data); } } @Override @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void addAll(Collection 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; } }
8. AppsLoader class
Next make a new class inside our package named AppsLoader.java and type this code:
package com.example.mylauncher; import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.support.v4.content.AsyncTaskLoader; public class AppsLoader extends AsyncTaskLoader> { ArrayList mInstalledApps; final PackageManager mPm; PackageIntentReceiver mPackageObserver; public AppsLoader(Context context) { super(context); mPm = context.getPackageManager(); } @Override public ArrayList loadInBackground() { // retrieve the list of installed applications List apps = mPm.getInstalledApplications(0); if (apps == null) { apps = new ArrayList (); } final Context context = getContext(); // create corresponding apps and load their labels ArrayList items = new ArrayList (apps.size()); for (int i = 0; i < apps.size(); i++) { String pkg = apps.get(i).packageName; // only apps which are launchable if (context.getPackageManager().getLaunchIntentForPackage(pkg) != null) { AppModel app = new AppModel(context, apps.get(i)); app.loadLabel(context); items.add(app); } } // sort the list Collections.sort(items, ALPHA_COMPARATOR); return items; } @Override public void deliverResult(ArrayList 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 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); } } @Override protected void onStartLoading() { if (mInstalledApps != null) { // If we currently have a result available, deliver it // immediately. deliverResult(mInstalledApps); } // watch for changes in app install and uninstall operation if (mPackageObserver == null) { mPackageObserver = new PackageIntentReceiver(this); } 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 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; } // Stop monitoring for changes. if (mPackageObserver != null) { getContext().unregisterReceiver(mPackageObserver); mPackageObserver = 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 apps) { // do nothing } /** * Perform alphabetical comparison of application entry objects. */ public static final Comparator ALPHA_COMPARATOR = new Comparator () { private final Collator sCollator = Collator.getInstance(); @Override public int compare(AppModel object1, AppModel object2) { return sCollator.compare(object1.getLabel(), object2.getLabel()); } }; }
9. PackageIntentReceiver class
Next make a new class inside our package named PackageIntentReceiver.java and type this code:
package com.example.mylauncher; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; public class PackageIntentReceiver extends BroadcastReceiver{ final AppsLoader mLoader; public PackageIntentReceiver(AppsLoader loader) { mLoader = loader; IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); mLoader.getContext().registerReceiver(this, filter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mLoader.getContext().registerReceiver(this, sdFilter); } @Override public void onReceive(Context context, Intent intent) { // Tell the loader about the change. mLoader.onContentChanged(); } }
10. list_item_icon_text.xml
Next, make a new .xml file named list_item_icon_text.xml inside res/layout folder. And type this code:
11. strings.xml
Edit strings.xml inside res/values folder with this code:
MyLauncher Settings Hello world! Dummy Button DUMMY\nCONTENT
12. AndroidManifest.xml
Finally, adit/or and some code in AndroidManifest.xml file with like this:
That's it. After all is done, right click on the project and choose "run as -> Android Application".
This is the screenshot of my result:
I hope this is useful. Beside, this launcher result is a very simple. But we can expand that project.
Bye...
/bye
Comments
Post a Comment