Initial Contribution
diff --git a/src/com/android/settings/ManageApplications.java b/src/com/android/settings/ManageApplications.java
new file mode 100644
index 0000000..8389502
--- /dev/null
+++ b/src/com/android/settings/ManageApplications.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import com.android.settings.R;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Activity to pick an application that will be used to display installation information and
+ * options to upgrade/uninstall/delete user data for system applications.
+ *  Initially a compute in progress message is displayed while the application retrieves
+ *  the size information of installed packages which is done asynchronously through a 
+ *  handler. Once the computation is done package resource information is retrieved 
+ *  and then the information is displayed on the screen. All 
+ *  messages are passed through a Handler object.
+ *  Known issue: There could be some ordering issues when installing/uninstalling
+ *  applications when the application list is being scanned.
+ */
+public class ManageApplications extends Activity implements SimpleAdapter.ViewBinder, OnItemClickListener {
+    private static final String TAG = "ManageApplications";
+    //Application prefix information
+    public static final String APP_PKG_PREFIX="com.android.settings.";
+    public static final String APP_PKG_NAME=APP_PKG_PREFIX+"ApplicationPkgName";
+    public static final String APP_PKG_SIZE= APP_PKG_PREFIX+"size";
+    public static final String APP_CHG=APP_PKG_PREFIX+"changed";
+    
+    //constant value that can be used to check return code from sub activity.
+    private static final int INSTALLED_APP_DETAILS = 1;
+     //application attributes passed to sub activity that displays more app info
+    private static final String KEY_APP_NAME = "ApplicationName";
+    private static final String KEY_APP_ICON = "ApplicationIcon";
+    private static final String KEY_APP_DESC = "ApplicationDescription";
+    private static final String KEY_APP_SIZE= "ApplicationSize";
+    //sort order that can be changed through the menu
+    public static final int SORT_ORDER_ALPHA = 0;
+    public static final int SORT_ORDER_SIZE = 1;
+   //key and resource values used in constructing map for SimpleAdapter
+    private static final String sKeys[] = new String[] { KEY_APP_NAME, KEY_APP_ICON, 
+            KEY_APP_DESC, KEY_APP_SIZE};
+    private static final int sResourceIds[] = new int[] { R.id.app_name, R.id.app_icon, 
+            R.id.app_description, R.id.app_size};
+    //List of ApplicationInfo objects for various applications
+    private List<ApplicationInfo> mAppList;
+    //SimpleAdapter used for managing items in the list
+    private SimpleAdapter mAppAdapter;
+    //map used to store size information which is used for displaying size information
+    //in this activity as well as the subactivity. this is to avoid invoking package manager
+    //api to retrieve size information
+    private HashMap<String, PackageStats> mSizeMap;
+    private HashMap<String, Map<String, ?> > mAppAdapterMap;
+    //sort order
+    private int mSortOrder = SORT_ORDER_ALPHA;
+    //log information boolean
+    private boolean localLOGV = Config.LOGV || false;
+    private ApplicationInfo mCurrentPkg;
+    private int mCurrentPkgIdx = 0;
+    private static final int COMPUTE_PKG_SIZE_START = 1;
+    private static final int COMPUTE_PKG_SIZE_DONE = 2;
+    private static final int REMOVE_PKG=3;
+    private static final int REORDER_LIST=4;
+    private static final int ADD_PKG=5;
+    private static final String ATTR_APP_IDX="ApplicationIndex";
+    private static final String ATTR_CHAINED="Chained";
+    private static final String ATTR_PKG_NAME="PackageName";
+    private PkgSizeObserver mObserver;
+    private PackageManager mPm;
+    private PackageIntentReceiver mReceiver;
+    private boolean mDoneIniting = false;
+    private String mKbStr;
+    private String  mMbStr;
+    private String mBStr;
+    
+    /*
+     * Handler class to handle messages for various operations
+     */
+    private Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            PackageStats ps;
+            ApplicationInfo info;
+            Bundle data;
+            String pkgName;
+            int idx;
+            int size;
+            boolean chained = false;
+            data = msg.getData();
+            switch (msg.what) {
+            case COMPUTE_PKG_SIZE_START:
+                mDoneIniting = false;
+                //initialize lists
+                mAppList = new ArrayList<ApplicationInfo>();
+                mSizeMap = new HashMap<String, PackageStats>();
+                mAppAdapterMap = new HashMap<String, Map<String, ?> >();
+                //update application list from PackageManager
+                mAppList = mPm.getInstalledApplications(0);
+                if(mAppList.size() == 0) {
+                    return;
+                }
+                mCurrentPkgIdx = 0;
+                mCurrentPkg = mAppList.get(0);
+                if(localLOGV) Log.i(TAG, "Initiating compute sizes for first time");
+                //register receiver
+                mReceiver = new PackageIntentReceiver();
+                mReceiver.registerReceiver();
+                pkgName = mCurrentPkg.packageName;
+                mObserver = new PkgSizeObserver(0);
+                mObserver.invokeGetSizeInfo(pkgName, true);
+                break;
+            case COMPUTE_PKG_SIZE_DONE:
+                ps = mObserver.ps;
+                info = mObserver.appInfo;
+                chained = data.getBoolean(ATTR_CHAINED);
+                if(!mObserver.succeeded) {
+                    if(chained) {
+                        removePackageFromAppList(ps.packageName);
+                    } else {
+                        //do not go to adding phase
+                        break;
+                    }
+                } else {
+                    //insert size value
+                    mSizeMap.put(ps.packageName, ps);
+                    Map<String, Object> entry = createMapEntry(mPm.getApplicationLabel(info), 
+                            mPm.getApplicationIcon(info), 
+                            info.loadDescription(mPm), 
+                            getSizeStr(ps));
+                    mAppAdapterMap.put(ps.packageName, entry);
+                }
+                if(chained) {
+                    //here app list is precomputed
+                    idx = data.getInt(ATTR_APP_IDX);
+                    //increment only if succeded
+                    if(mObserver.succeeded) {
+                        idx++;
+                    }
+                    if(idx <  mAppList.size()) {
+                        pkgName = mAppList.get(idx).packageName;
+                        //increment record index and invoke getSizeInfo for next record
+                        mObserver.invokeGetSizeInfo(pkgName, true);
+                    } else {
+                        sortAppList();
+                        createListFromValues();
+                        mDoneIniting = true;
+                    }
+                } else {
+                    //add app info object as well
+                    mAppList.add(info);
+                    sortAppList();
+                    size = mAppList.size();
+                    int i;
+                    for(i = 0; i < size; i++) {
+                        if(mAppList.get(i).packageName.equalsIgnoreCase(mCurrentPkg.packageName)) {
+                            if(i > mCurrentPkgIdx) {
+                                mCurrentPkgIdx = i;
+                            }
+                            break;
+                        }
+                    }
+                    createListFromValues();
+                }
+                break;
+            case REMOVE_PKG:
+                if(!mDoneIniting) {
+                    //insert message again after some delay
+                    sendMessageToHandler(REMOVE_PKG, data, 10*1000);
+                    break;
+                }
+                pkgName = data.getString(ATTR_PKG_NAME);
+                removePackageFromAppList(pkgName);
+                if(mSizeMap.remove(pkgName) == null) {
+                    Log.i(TAG, "Coudnt remove from size map package:"+pkgName);
+                }
+                if(mAppAdapterMap.remove(pkgName) == null) {
+                    Log.i(TAG, "Coudnt remove from app adapter map package:"+pkgName);
+                }
+                if(mCurrentPkg.packageName.equalsIgnoreCase(pkgName)) {
+                    if(mCurrentPkgIdx == (mAppList.size()-1)) {
+                        mCurrentPkgIdx--;
+                    }
+                    mCurrentPkg = mAppList.get(mCurrentPkgIdx);
+                }
+                createListFromValues();
+                break;
+            case REORDER_LIST:
+                int sortOrder = msg.arg1;
+                if(sortOrder != mSortOrder) {
+                    mSortOrder = sortOrder;
+                    if(localLOGV) Log.i(TAG, "Changing sort order to "+mSortOrder);
+                    sortAppList();
+                    mCurrentPkgIdx  = 0;
+                    mCurrentPkg = mAppList.get(mCurrentPkgIdx);
+                    createListFromValues();
+                }
+                break;
+            case ADD_PKG:
+                pkgName = data.getString(ATTR_PKG_NAME);
+                if(!mDoneIniting) {
+                   //insert message again after some delay
+                    sendMessageToHandler(ADD_PKG, data, 10*1000);
+                    break;
+                }
+                mObserver.invokeGetSizeInfo(pkgName, false);
+                break;
+            default:
+                break;
+            }
+        }
+    };
+    
+    private void removePackageFromAppList(String pkgName) {
+        int size = mAppList.size();
+        for(int i = 0; i < size; i++) {
+            if(mAppList.get(i).packageName.equalsIgnoreCase(pkgName)) {
+                mAppList.remove(i);
+                break;
+            }
+        }
+    }
+    
+    private void clearMessages() {
+        synchronized(mHandler) {
+            mHandler.removeMessages(COMPUTE_PKG_SIZE_START);
+            mHandler.removeMessages(COMPUTE_PKG_SIZE_DONE);
+            mHandler.removeMessages(REMOVE_PKG);
+            mHandler.removeMessages(REORDER_LIST);
+            mHandler.removeMessages(ADD_PKG);
+        }
+    }
+    
+    private void sendMessageToHandler(int msgId, Bundle data, long delayMillis) {
+        synchronized(mHandler) {
+            Message msg = mHandler.obtainMessage(msgId);
+            msg.setData(data);
+            if(delayMillis == 0) {
+                mHandler.sendMessage(msg);
+            } else {
+                mHandler.sendMessageDelayed(msg, delayMillis);
+            }
+        }
+    }
+    
+    private void sendMessageToHandler(int msgId, int arg1) {
+        synchronized(mHandler) {
+            Message msg = mHandler.obtainMessage(msgId);
+            msg.arg1 = arg1;
+            mHandler.sendMessage(msg);
+        }
+    }
+    
+    private void sendMessageToHandler(int msgId) {
+        synchronized(mHandler) {
+            mHandler.sendEmptyMessage(msgId);
+        }
+    }
+    
+    class PkgSizeObserver extends IPackageStatsObserver.Stub {
+        public PackageStats ps;
+        public ApplicationInfo appInfo;
+        public Drawable appIcon;
+        public CharSequence appName;
+        public CharSequence appDesc = "";
+        private int mIdx = 0;
+        private boolean mChained = false;
+        public boolean succeeded;
+        PkgSizeObserver(int i) {
+            mIdx = i;
+        }
+        
+        private void getAppDetails() {
+            try {
+                appInfo = mPm.getApplicationInfo(ps.packageName, 0);
+            } catch (NameNotFoundException e) {
+                return;
+            }
+            appName = appInfo.loadLabel(mPm);
+            appIcon = appInfo.loadIcon(mPm);
+        }
+        
+        public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
+            Bundle data = new Bundle();
+            ps = pStats;
+            succeeded = pSucceeded;
+            if(mChained) {
+                data.putInt(ATTR_APP_IDX, mIdx);
+                if(succeeded) {
+                    mIdx++;
+                }
+            }
+            data.putBoolean(ATTR_CHAINED, mChained);
+            getAppDetails();
+            if(localLOGV) Log.i(TAG, "onGetStatsCompleted::"+appInfo.packageName+", ("+ps.cacheSize+","+
+                    ps.codeSize+", "+ps.dataSize);
+            sendMessageToHandler(COMPUTE_PKG_SIZE_DONE, data, 0);
+        }
+        
+        public void invokeGetSizeInfo(String packageName, boolean chained) {
+             mChained = chained;
+             mPm.getPackageSizeInfo(packageName, this);
+        }
+    }
+    
+    /**
+     * Receives notifications when applications are added/removed.
+     */
+    private class PackageIntentReceiver extends BroadcastReceiver {
+         void registerReceiver() {
+             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+             filter.addDataScheme("package");
+             ManageApplications.this.registerReceiver(this, filter);
+         }
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String actionStr = intent.getAction();
+            Uri data = intent.getData();
+            String pkgName = data.getEncodedSchemeSpecificPart();
+            if(localLOGV) Log.i(TAG, "action:"+actionStr+", for package:"+pkgName);
+            updatePackageList(actionStr, pkgName);
+        }
+    }
+    
+    private void updatePackageList(String actionStr, String pkgName) {
+        //technically we dont have to invoke handler since onReceive is invoked on
+        //the main thread but doing it here for better clarity
+        if(Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) {
+            Bundle data = new Bundle();
+            data.putString(ATTR_PKG_NAME, pkgName);
+            sendMessageToHandler(ADD_PKG, data, 0);
+        } else if(Intent.ACTION_PACKAGE_REMOVED.equalsIgnoreCase(actionStr)) {
+            Bundle data = new Bundle();
+            data.putString(ATTR_PKG_NAME, pkgName);
+            sendMessageToHandler(REMOVE_PKG, data, 0);
+        } else if(Intent.ACTION_PACKAGE_CHANGED.equalsIgnoreCase(actionStr)) {
+            //force adapter to draw the list again. TODO derive from SimpleAdapter
+            //to avoid this
+           
+        }   
+    }
+    
+    /*
+     * Utility method to create an array of map objects from a map of map objects
+     *  for displaying list items to be used in SimpleAdapter.
+     */
+    private void createListFromValues() {
+        findViewById(R.id.center_text).setVisibility(View.GONE);
+        populateAdapterList();
+        mAppAdapter.setViewBinder(this);
+        ListView lv= (ListView) findViewById(android.R.id.list);
+        lv.setOnItemClickListener(this);
+        lv.setAdapter(mAppAdapter);
+        if(mCurrentPkgIdx != -1) {
+            lv.setSelection(mCurrentPkgIdx);
+        }
+    }
+    
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        String action = getIntent().getAction();
+        if(action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
+            mSortOrder = SORT_ORDER_SIZE;
+        }
+        mPm = getPackageManager();
+        //load strings from resources
+        mBStr = getString(R.string.b_text);
+        mKbStr = getString(R.string.kb_text);
+        mMbStr = getString(R.string.mb_text);
+    }
+    
+    @Override
+    public void onStart() {
+        super.onStart();
+        setContentView(R.layout.compute_sizes);
+        //clear all messages related to application list
+        clearMessages();
+        sendMessageToHandler(COMPUTE_PKG_SIZE_START);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        //register receiver here
+        unregisterReceiver(mReceiver);        
+    }
+    
+    public static class AppInfoComparator implements Comparator<ApplicationInfo> {
+        public AppInfoComparator(HashMap<String, PackageStats> pSizeMap) {
+            mSizeMap= pSizeMap;
+        }
+
+        public final int compare(ApplicationInfo a, ApplicationInfo b) {
+            PackageStats aps, bps;
+            aps = mSizeMap.get(a.packageName);
+            bps = mSizeMap.get(b.packageName);
+            if (aps == null && bps == null) {
+                return 0;
+            } else if (aps == null) {
+                return 1;
+            } else if (bps == null) {
+                return -1;
+            }
+            long atotal = aps.dataSize+aps.codeSize+aps.cacheSize;
+            long btotal = bps.dataSize+bps.codeSize+bps.cacheSize;
+            long ret = atotal-btotal;
+            //negate result to sort in descending order
+            if(ret < 0) {
+                return 1;
+            }
+            if(ret == 0) {
+                return 0;
+            }
+            return -1;
+        }
+        private HashMap<String, PackageStats> mSizeMap;
+    }
+
+    /*
+     * Have to extract elements form map and populate a list ot be used by
+     * SimpleAdapter when displaying list elements. The sort order has to follow
+     * the order of elements in mAppList.
+     */
+     private List<Map<String, ?>> createAdapterListFromMap() {
+         //get the index from mAppInfo which gives the correct sort position
+         int imax = mAppList.size();
+         if(localLOGV) Log.i(TAG, "Creating new adapter list");
+         List<Map<String, ?>> adapterList = new ArrayList<Map<String, ?>>();
+         ApplicationInfo tmpInfo;
+         for(int i = 0; i < imax; i++) {
+             tmpInfo = mAppList.get(i);
+             Map<String, Object>newObj = new TreeMap<String, Object>(
+                     mAppAdapterMap.get(tmpInfo.packageName));
+             adapterList.add(newObj);
+         }
+         return adapterList;
+     }
+     
+    private void populateAdapterList() {
+        mAppAdapter = new SimpleAdapter(this, createAdapterListFromMap(),
+                    R.layout.manage_applications_item, sKeys, sResourceIds);
+    }
+    
+    private String getSizeStr(PackageStats ps) {
+        String retStr = "";
+        //insert total size information into map to display in view
+        //at this point its guaranteed that ps is not null. but checking anyway
+        if(ps != null) {
+            long size = ps.cacheSize+ps.codeSize+ps.dataSize;
+            if(size < 1024) {
+                return String.valueOf(size)+mBStr;
+            }
+            long kb, mb, rem;
+            kb = size >> 10;
+            rem = size - (kb << 10);
+            if(kb < 1024) {
+                if(rem > 512) {
+                    kb++;
+                }
+                retStr += String.valueOf(kb)+mKbStr;
+                return retStr;
+            }
+            mb = kb >> 10;
+            if(kb >= 512) {
+                //round off
+                mb++;
+            }
+            retStr += String.valueOf(mb)+ mMbStr;
+            return retStr;
+        } else {
+            Log.w(TAG, "Something fishy, cannot find size info for package:"+ps.packageName);
+        }
+        return retStr;
+    }
+    
+    public void sortAppList() {
+        // Sort application list
+        if(mSortOrder == SORT_ORDER_ALPHA) {
+            Collections.sort(mAppList, new ApplicationInfo.DisplayNameComparator(mPm));
+        } else if(mSortOrder == SORT_ORDER_SIZE) {
+            Collections.sort(mAppList, new AppInfoComparator(mSizeMap));
+        }
+    }
+    
+    private Map<String, Object> createMapEntry(CharSequence appName, 
+            Drawable appIcon, CharSequence appDesc, String sizeStr) {
+        Map<String, Object> map = new TreeMap<String, Object>();
+        map.put(KEY_APP_NAME, appName);
+        //the icon cannot be null. if the application hasnt set it, the default icon is returned.
+        map.put(KEY_APP_ICON, appIcon);
+        if(appDesc == null) {
+            appDesc="";
+        }
+        map.put(KEY_APP_DESC, appDesc);
+        map.put(KEY_APP_SIZE, sizeStr);
+        return map;
+    }
+    
+    private void startApplicationDetailsActivity(ApplicationInfo info, PackageStats ps) {
+        //Create intent to start new activity
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setClass(this, InstalledAppDetails.class);
+        intent.putExtra(APP_PKG_NAME, info.packageName);
+        if(localLOGV) Log.i(TAG, "code="+ps.codeSize+", cache="+ps.cacheSize+", data="+ps.dataSize);
+        intent.putExtra(APP_PKG_SIZE,  ps);
+        if(localLOGV) Log.i(TAG, "Starting sub activity to display info for app:"+info
+                +" with intent:"+intent);
+        //start new activity to display extended information
+        if ((info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+        }
+        startActivityForResult(intent, INSTALLED_APP_DETAILS);
+    }
+    
+    public boolean setViewValue(View view, Object data, String textRepresentation) {
+        if(data == null) {
+            return false;
+        }
+        int id = view.getId();
+        switch(id) {
+        case R.id.app_name:
+            ((TextView)view).setText((String)data);
+            break;
+        case R.id.app_icon:
+            ((ImageView)view).setImageDrawable((Drawable)data);
+            break;
+        case R.id.app_description:
+            ((TextView)view).setText((String)data);
+            break;
+        case R.id.app_size:
+            ((TextView)view).setText((String)data);
+            break;
+        default:
+                break;
+        }
+        return true;
+    }
+    
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        menu.add(0, SORT_ORDER_ALPHA, 0, R.string.sort_order_alpha)
+                .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
+        menu.add(0, SORT_ORDER_SIZE, 0, R.string.sort_order_size)
+                .setIcon(android.R.drawable.ic_menu_sort_by_size);
+        return true;
+    }
+    
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        if(mDoneIniting) {
+            menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
+            menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder!= SORT_ORDER_SIZE);
+            return true;
+        } 
+        return false;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        int menuId = item.getItemId();
+        sendMessageToHandler(REORDER_LIST, menuId);
+        return true;
+    }
+
+    public void onItemClick(AdapterView<?> parent, View view, int position,
+            long id) {
+        mCurrentPkgIdx=position;
+        ApplicationInfo info = mAppList.get(position);
+        mCurrentPkg = info;        
+        PackageStats ps = mSizeMap.get(info.packageName);
+        startApplicationDetailsActivity(info, ps);
+    }
+}