Make Custom Android Launcher

There are thousands of Android Launcher available in Play Store. Each and every Android mobile user loves to customize their mobile with different Android Launcher. If you want to develop your own launcher, this post helps you to fulfill your wish. In this post, you will find a demo launcher with layout and code which include many features like Drag & Drop, rearrange the installed application, change the background wallpaper etc.
Here is an example of demo launcher which display all the installed applications in 3*3 Grid View format compatible to all devices.
First of all we need to find all the installed applications from device so we should implement a subclass of AsyncTaskLoader that loads the currently installed applications from the package manager. If you want to display all the installed applications in  List View you can use official Android Developer reference.
To get the complete Eclipse project, Click Here.
Before going to implement a subclass of AsyncTaskLoader, We must create a class named "AppEntry" that holds each data of an installed application.
//***AppEntry.class*** 
public class AppEntry {

     private final AppLoader mLoader;
     private final ApplicationInfo mInfo;
     private final File mApkFile;
     private String mLabel;
     private Drawable mIcon;
     private boolean mMounted;
     
 public AppEntry(AppLoader appLoader, ApplicationInfo info) {
  mLoader=appLoader;
  mInfo = info;
                mApkFile = new File(info.sourceDir);
 }

 void loadLabel(Context context) {
        if (mLabel == null || !mMounted) {
            if (!mApkFile.exists()) {
                mMounted = false;
                mLabel = mInfo.packageName;
            } else {
                mMounted = true;
                CharSequence label = mInfo.loadLabel(context.getPackageManager());
                mLabel = label != null ? label.toString() : mInfo.packageName;
            }
        }
    }
 
 public ApplicationInfo getApplicationInfo() {
        return mInfo;
    }

    public String getLabel() {
        return mLabel;
    }
    
    public Drawable getIcon() {
     if (mIcon == null) {
      if (mApkFile.exists()) {
       mIcon = mInfo.loadIcon(mLoader.mPm);
       return mIcon;
       } else {
        mMounted = false;
        }
      } else if (!mMounted) {
        if (mApkFile.exists()) {
            mMounted = true;
            mIcon = mInfo.loadIcon(mLoader.mPm);
            return mIcon;
        }
    } else {
        return mIcon;
    }
    return mLoader.getContext().getResources().getDrawable(
            android.R.drawable.sym_def_app_icon);
    }
    
    @Override 
    public String toString() {
        return mLabel;
    }
    
}
Now we need a class that helps to update the loader whenever user delete or install new app so make a subclass of BroadcastReceiver which filter the intent by ACTION_PACKAGE_ADDED,  _REMOVED and _CHANGED. If there's any application installed in external storage, then we need to register an event for that application using Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE or _UNAVAILABLE.
//***PackageIntentReceiver.class*** 
public class PackageIntentReceiver extends BroadcastReceiver {
 
 final AppLoader mLoader;

    public PackageIntentReceiver(AppLoader 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);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        mLoader.onContentChanged();
    }
    
}
Here is a custom loader class which is subclass of AsyncTaskLoader<> class that loads all installed apps and store application info, label and icon of a specific app in AppEntry class.
//***AppLoader.class***
public class AppLoader extends AsyncTaskLoader<List<AppEntry>> {

    final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();
    final PackageManager mPm;
    List<AppEntry> mApps;
    PackageIntentReceiver mPackageObserver;

 public AppLoader(Context context) {
  super(context);
  mPm = getContext().getPackageManager();
 }
 @Override
 public List<AppEntry> loadInBackground() {
  List<ApplicationInfo> apps = mPm.getInstalledApplications(PackageManager.GET_GIDS);
  if (apps == null) {
                apps = new ArrayList<ApplicationInfo>();
        }
  final Context context = getContext();
  List<AppEntry> entries = new ArrayList<AppEntry>();
        
        for (ApplicationInfo app : apps) {
            if(mPm.getLaunchIntentForPackage(app.packageName) != null) {
                // get all apps with launcher intent
                if((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 1) {
                    //get all updated system apps

                } else if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 1) {
                    // system apps

                } else {
                    //get all user installed apps
                }
             AppEntry entry = new AppEntry(this, app);
             entry.loadLabel(context);
             entries.add(entry);
            }

        }
        Collections.sort(entries, ALPHA_COMPARATOR);
        return entries;
 }
 
 public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
     private final Collator sCollator = Collator.getInstance();
     @Override
     public int compare(AppEntry object1, AppEntry object2) {
         return sCollator.compare(object1.getLabel(), object2.getLabel());
     }
 };
 
 @Override
 public void deliverResult(List<AppEntry> apps) {
        if (isReset()) {
            if (apps != null) {
                onReleaseResources(apps);
            }
        }
        List<AppEntry> oldApps = mApps;
        mApps = apps;

        if (isStarted()) {
            super.deliverResult(apps);
        }
        if (oldApps != null) {
            onReleaseResources(oldApps);
        }
    }
    @Override 
    protected void onStartLoading() {
        if (mApps != null) {
            deliverResult(mApps);
        }
        
        if (mPackageObserver == null) {
            mPackageObserver = new PackageIntentReceiver(this);
        }

        boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());

        if (takeContentChanged() || mApps == null || configChange) {
            forceLoad();
        }
    }
    @Override protected void onStopLoading() {
        cancelLoad();
    }
    @Override public void onCanceled(List<AppEntry> apps) {
        super.onCanceled(apps);
        onReleaseResources(apps);
    }
    @Override protected void onReset() {
        super.onReset();
        onStopLoading();
        if (mApps != null) {
            onReleaseResources(mApps);
            mApps = null;
        }
        if (mPackageObserver != null) {
            getContext().unregisterReceiver(mPackageObserver);
            mPackageObserver = null;
        }
    }
    protected void onReleaseResources(List<AppEntry> apps) {
    }
}
Now it's time to make a custom pagerAdapter which add pages to ViewPager. ViewPager class is available on android.support.v4.view.ViewPager package.
//***MyPagerAdapter.class***
public class MyPagerAdapter extends FragmentStatePagerAdapter {

 private List<GridFragment> gridFragments;
 
 public MyPagerAdapter(FragmentManager fm, List<GridFragment> gridFragments) {
  super(fm);
  this.gridFragments = gridFragments;
 }

 @Override
 public Fragment getItem(int arg0) {
  return this.gridFragments.get(arg0);
 }

 @Override
 public int getCount() {
  return this.gridFragments.size();
 }

}
Let's take a look at the Main Activity of our launcher. The main activity class named EaseActivity should be a subclass of FragmentActivity class and should implements LoaderManager.LoaderCallbacks<List<AppEntry>>. This class gets all apps from Loader and make a GridPage named GridFragment with 9 apps per page. At last, all these GridFragments are added to the ViewPager.
//***EaseActivity.class***
public class EaseActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<List<AppEntry>> {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_ease);
  getSupportLoaderManager().initLoader(0, null, this);
 }
 @Override
 public Loader<List<AppEntry>> onCreateLoader(
   int arg0, Bundle arg1) {
  return new AppLoader(this);
 }

 private ViewPager pager;
 private MyPagerAdapter pagerAdapter;
 @Override
 public void onLoadFinished(Loader<List<AppEntry>> loader,
   List<AppEntry> appEntries) {
  
  Iterator<AppEntry> iterator= appEntries.iterator();
  List<GridFragment> gridFragments= new ArrayList<GridFragment>();
  
  pager =(ViewPager)findViewById(R.id.pager);
  
  int i=0;
  AppEntry entry;
  while (iterator.hasNext()) {
   ArrayList<GridItems> gridItems=new ArrayList<GridItems>();
   
   entry = iterator.next();
   GridItems item=new GridItems(0, entry.getLabel(), entry.getIcon(), entry.getApplicationInfo());
   gridItems.add(item);
   i=i+1;
   
   if (iterator.hasNext()) {
    entry = iterator.next();
    GridItems item1=new GridItems(1, entry.getLabel(), entry.getIcon(), entry.getApplicationInfo());
    gridItems.add(item1);
    i=i+1;
   }
   if (iterator.hasNext()) {
    entry = iterator.next();
    GridItems item2=new GridItems(2, entry.getLabel(), entry.getIcon(), entry.getApplicationInfo());
    gridItems.add(item2);
    i=i+1;
   }
   if (iterator.hasNext()) {
    entry = iterator.next();
    GridItems item3=new GridItems(3, entry.getLabel(), entry.getIcon(), entry.getApplicationInfo());
    gridItems.add(item3);
    i=i+1;
   }
   if (iterator.hasNext()) {
    entry = iterator.next();
    GridItems item4=new GridItems(4, entry.getLabel(), entry.getIcon(), entry.getApplicationInfo());
    gridItems.add(item4);
    i=i+1;
   }
   if (iterator.hasNext()) {
    entry = iterator.next();
    GridItems item5=new GridItems(5, entry.getLabel(), entry.getIcon(), entry.getApplicationInfo());
    gridItems.add(item5);
    i=i+1;
   }
   if (iterator.hasNext()) {
    entry = iterator.next();
    GridItems item6=new GridItems(6, entry.getLabel(), entry.getIcon(), entry.getApplicationInfo());
    gridItems.add(item6);
    i=i+1;
   }
   if (iterator.hasNext()) {
    entry = iterator.next();
    GridItems item7=new GridItems(7, entry.getLabel(), entry.getIcon(), entry.getApplicationInfo());
    gridItems.add(item7);
    i=i+1;
   }
   if (iterator.hasNext()) {
    entry = iterator.next();
    GridItems item8=new GridItems(8, entry.getLabel(), entry.getIcon(), entry.getApplicationInfo());
    gridItems.add(item8);
    i=i+1;
   }
   
   GridItems[] gp = {};
   GridItems[] gridPage = gridItems.toArray(gp);
   gridFragments.add(new GridFragment(gridPage, EaseActivity.this));
  }
  pagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), gridFragments);
  pager.setAdapter(pagerAdapter);
 }

 @Override
 public void onLoaderReset(Loader<List<AppEntry>> arg0) {  
 }
}
Here is the GridFragment class. This class works as a page and apps are added to this page using GridAdapter. The drag and drop feature defined in this class.
//***GridFragment.class***
public class GridFragment extends Fragment {

  private GridView mGridView;
  private GridView dockView;
  private GridAdapter mGridAdapter;
  GridItems[] gridItems = {};
  private Activity mainActivity;
  
 public GridFragment(GridItems[] gridItems, Activity mainActivity) {
  this.gridItems = gridItems;
  this.mainActivity = mainActivity;
 }
 
 @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
   View view;
   view = inflater.inflate(R.layout.grid_page, container, false);
   mGridView = (GridView) view.findViewById(R.id.gridView);
   dockView = (GridView) view.findViewById(R.id.dock);
   
   float scalefactor = getResources().getDisplayMetrics().density * 100;
   int number = mainActivity.getWindowManager().getDefaultDisplay().getWidth();
   int columns = (int) ((float) number / (float) scalefactor);
   mGridView.setNumColumns(3);
   return view;
  }
 
  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
   super.onActivityCreated(savedInstanceState);
  
   if (mainActivity != null) {
  
    mGridAdapter = new GridAdapter(mainActivity, gridItems);
    
    if (mGridView != null) {
     mGridView.setAdapter(mGridAdapter);
    }
  
    mGridView.setOnItemClickListener(new OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView parent, View view,
       int position, long id) {
      onGridItemClick((GridView) parent, view, position, id);
     }
    });
    
    mGridView.setOnItemLongClickListener(new OnItemLongClickListener() {

  @Override
  public boolean onItemLongClick(AdapterView<?> parent, View view,
    int position, long id) {
   dragANDdrop(parent,view,position,id);
   return false;
  }
    });
    }
   }

  protected void dragANDdrop(AdapterView<?> parent, View view,
    int position, long id) {

                 int count = parent.getChildCount();
                 for (int i = 0; i < count; i++) {
                     View curr = parent.getChildAt(i);
                     curr.setOnDragListener(new View.OnDragListener() {
      
      @Override
      public boolean onDrag(View v, DragEvent event) {
       boolean result = true;
                            int action = event.getAction();
                            switch (action) 
                            {
                            case DragEvent.ACTION_DRAG_STARTED:
                                break;
                            case DragEvent.ACTION_DRAG_LOCATION:
                                break;
                            case DragEvent.ACTION_DRAG_ENTERED:
                                break;
                            case DragEvent.ACTION_DRAG_EXITED:
                                break;
                            case DragEvent.ACTION_DROP:
                             if(event.getLocalState()==v){result=false;}
                             else{
                              
                              View droped=(View)event.getLocalState();
                              GridItems dropItem = ((ViewHolder)droped.getTag()).item;
                              
                              GridView parent= (GridView)droped.getParent();
                              GridAdapter gridAdapter = (GridAdapter)parent.getAdapter();
                              GridItems[] gridItemss = gridAdapter.getItems();
                              
                              View target=v;
                              
                              GridItems targetItem = ((ViewHolder)target.getTag()).item;
                              List<GridItems> list = new ArrayList<GridItems>(Arrays.asList(gridItemss));
                              int index = list.indexOf(targetItem);
                              list.remove(dropItem);
                              list.add(index, dropItem);
                              
                              gridItemss = list.toArray(gridItemss);
                              gridAdapter.notifyDataSetChanged();
                             }
                                break;
                            case DragEvent.ACTION_DRAG_ENDED:
                                break;
                            default:
                                result = false;
                                break;
                            }
       return result;
      }
     });
                 }
                 int relativePosition = position - parent.getFirstVisiblePosition();
                 View target=(View)parent.getChildAt(relativePosition);
                 
                 ViewHolder holder= ((ViewHolder)target.getTag());
                 GridItems currItem=holder.item;
                 String path=currItem.title;
                 ClipData clipData=ClipData.newPlainText("DragData", path);
                 target.startDrag(clipData, new View.DragShadowBuilder(target), target, 0);
             
 }

 public void onGridItemClick(GridView g, View v, int position, long id) {
  ApplicationInfo info = gridItems[position].info;
  Intent intent= getActivity().getPackageManager().getLaunchIntentForPackage(info.packageName);
  startActivity(intent);
  } 
}
A GridAdapter class that fill apps data in GridFragment class.
//***GridAdapter.class***
public class GridAdapter extends BaseAdapter {

 Context context;
  
  public class ViewHolder {
    public ImageView imageView;
    public TextView textTitle;
    public GridItems item;
   }
  private GridItems[] items;
  private LayoutInflater mInflater;
  
  public GridAdapter(Context context, GridItems[] locations) {
   mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    this.context = context;
    items = locations;
   }

  public GridItems[] getItems() {
   return items;
  }
  
  public void setItems(GridItems[] items) {
   this.items = items;
  }
  
  @Override
  public int getCount() {
   if (items != null) {
    return items.length;
   }
   return 0;
  }
  
  @Override
  public void notifyDataSetChanged() {
   super.notifyDataSetChanged();
  }
  
  @Override
  public Object getItem(int position) {
   if (items != null && position >= 0 && position < getCount()) {
    return items[position];
   }
   return null;
  }
  
  @Override
  public long getItemId(int position) {
   if (items != null && position >= 0 && position < getCount()) {
    return items[position].id;
   }
   return 0;
  }
  
  public void setItemsList(GridItems[] locations) {
   this.items = locations;
  }
  
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
  
   View view = convertView;
   ViewHolder viewHolder;
  
   if (view == null) {
  view = mInflater.inflate(R.layout.show_app, parent, false);
  viewHolder = new ViewHolder();
  viewHolder.imageView = (ImageView) view.findViewById(R.id.grid_item_image);
  viewHolder.textTitle = (TextView) view.findViewById(R.id.grid_item_label);
  view.setTag(viewHolder);
  } 
   else 
  {
   viewHolder = (ViewHolder) view.getTag();
  }
   
   GridItems gridItems = items[position];
   viewHolder.item = items[position];
   viewHolder.imageView.setImageDrawable(gridItems.drawable);
   viewHolder.textTitle.setText(gridItems.title);
   return view;
  }
 }
To get the complete Eclipse project, Click Here.
Screenshots:

Comments

Popular posts from this blog

Layouts for Fragment