From c0b23d3f0bb42b26950512590d5ebd6d3373032d Mon Sep 17 00:00:00 2001 From: Suchi Amalapurapu Date: Thu, 20 Aug 2009 01:27:19 -0700 Subject: [PATCH] Add type to filter option to list view Make ListAdapter implement Filterable. Add new implementation for Filter to search based on prefixes. When creating the list include the filter option to create actual list entries. Since Filter.performFiltering is done on a work thread, introduce a new lock and a HashMap of packages matching a prefix which is then used to create a filtered list of applications. Also separte out modifying mAppLocalList which is the basis for list and the dependent mFilterMap used by Filter.performFiltering method to Locked methods for clarity. Note that mAppLocalList is manipulated on main UI thread everywhere but mFilterMap is not. --- .../android/settings/ManageApplications.java | 163 +++++++++++++++--- 1 file changed, 137 insertions(+), 26 deletions(-) diff --git a/src/com/android/settings/ManageApplications.java b/src/com/android/settings/ManageApplications.java index 2222b658737..b8858f95f5d 100644 --- a/src/com/android/settings/ManageApplications.java +++ b/src/com/android/settings/ManageApplications.java @@ -52,6 +52,8 @@ import android.view.ViewGroup; import android.view.Window; import android.widget.AdapterView; import android.widget.BaseAdapter; +import android.widget.Filter; +import android.widget.Filterable; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; @@ -653,19 +655,29 @@ public class ManageApplications extends ListActivity implements return installedAppList; } } + + private static boolean matchFilter(boolean filter, Map filterMap, String pkg) { + boolean add = true; + if (filter) { + if (filterMap == null || !filterMap.containsKey(pkg)) { + add = false; + } + } + return add; + } /* * Utility method used to figure out list of apps based on filterOption * If the framework supports an additional flag to indicate running apps * we can get away with some code here. */ - List getFilteredApps(List pAppList, int filterOption) { + List getFilteredApps(List pAppList, int filterOption, boolean filter, + Map filterMap) { List retList = new ArrayList(); if(pAppList == null) { return retList; } if (filterOption == FILTER_APPS_THIRD_PARTY) { - List appList =new ArrayList (); for (ApplicationInfo appInfo : pAppList) { boolean flag = false; if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { @@ -676,15 +688,16 @@ public class ManageApplications extends ListActivity implements flag = true; } if (flag) { - appList.add(appInfo); + if (matchFilter(filter, filterMap, appInfo.packageName)) { + retList.add(appInfo); + } } } - return appList; + return retList; } else if (filterOption == FILTER_APPS_RUNNING) { - List appList =new ArrayList (); List procList = getRunningAppProcessesList(); if ((procList == null) || (procList.size() == 0)) { - return appList; + return retList; } // Retrieve running processes from ActivityManager HashMap runningMap = @@ -700,15 +713,22 @@ public class ManageApplications extends ListActivity implements // Query list to find running processes in current list for (ApplicationInfo appInfo : pAppList) { if (runningMap.get(appInfo.packageName) != null) { - appList.add(appInfo); + if (matchFilter(filter, filterMap, appInfo.packageName)) { + retList.add(appInfo); + } } } - return appList; + return retList; } else { - return pAppList; + for (ApplicationInfo appInfo : pAppList) { + if (matchFilter(filter, filterMap, appInfo.packageName)) { + retList.add(appInfo); + } + } + return retList; } } - + private List getRunningAppProcessesList() { ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); return am.getRunningAppProcesses(); @@ -927,11 +947,63 @@ public class ManageApplications extends ListActivity implements * the getId methods via the package name into the internal maps and indices. * The order of applications in the list is mirrored in mAppLocalList */ - class AppInfoAdapter extends BaseAdapter { + class AppInfoAdapter extends BaseAdapter implements Filterable { private List mAppList; private List mAppLocalList; + private Map mFilterMap = new HashMap(); AlphaComparator mAlphaComparator = new AlphaComparator(); SizeComparator mSizeComparator = new SizeComparator(); + private Filter mAppFilter = new AppFilter(); + final private Object mFilterLock = new Object(); + private Map mCurrentFilterMap = null; + + private void generateFilterListLocked(List list) { + mAppLocalList = new ArrayList(list); + synchronized(mFilterLock) { + for (ApplicationInfo info : mAppLocalList) { + String label = info.packageName; + AppInfo aInfo = mCache.getEntry(info.packageName); + if ((aInfo != null) && (aInfo.appName != null)) { + label = aInfo.appName.toString(); + } + mFilterMap.put(info.packageName, label.toLowerCase()); + } + } + } + + private void addFilterListLocked(int newIdx, ApplicationInfo info, CharSequence pLabel) { + mAppLocalList.add(newIdx, info); + synchronized (mFilterLock) { + String label = info.packageName; + if (pLabel != null) { + label = pLabel.toString(); + } + mFilterMap.put(info.packageName, label.toLowerCase()); + } + } + + private boolean removeFilterListLocked(String removePkg) { + // Remove from filtered list + int N = mAppLocalList.size(); + int i; + for (i = (N-1); i >= 0; i--) { + ApplicationInfo info = mAppLocalList.get(i); + if (info.packageName.equalsIgnoreCase(removePkg)) { + if (localLOGV) Log.i(TAG, "Removing " + removePkg + " from local list"); + mAppLocalList.remove(i); + synchronized (mFilterLock) { + mFilterMap.remove(removePkg); + } + return true; + } + } + return false; + } + + private void reverseGenerateList() { + generateFilterListLocked(getFilteredApps(mAppList, mFilterApps, mCurrentFilterMap!= null, mCurrentFilterMap)); + sortListInner(mSortOrder); + } // Make sure the cache or map contains entries for all elements // in appList for a valid sort. @@ -942,11 +1014,11 @@ public class ManageApplications extends ListActivity implements // Just refresh the list appList = mAppList; } else { - mAppList = pAppList; + mAppList = new ArrayList(pAppList); appList = pAppList; notify = true; } - mAppLocalList = getFilteredApps(appList, filterOption); + generateFilterListLocked(getFilteredApps(appList, filterOption, mCurrentFilterMap!= null, mCurrentFilterMap)); // This loop verifies and creates new entries for new packages in list int imax = appList.size(); for (int i = 0; i < imax; i++) { @@ -1083,7 +1155,7 @@ public class ManageApplications extends ListActivity implements public void sortBaseList(int sortOrder) { if (localLOGV) Log.i(TAG, "Sorting base list based on sortOrder = "+sortOrder); sortAppList(mAppList, sortOrder); - mAppLocalList = getFilteredApps(mAppList, mFilterApps); + generateFilterListLocked(getFilteredApps(mAppList, mFilterApps, mCurrentFilterMap!= null, mCurrentFilterMap)); adjustIndex(); } @@ -1106,7 +1178,7 @@ public class ManageApplications extends ListActivity implements */ public boolean resetAppList(int filterOption) { // Change application list based on filter option - mAppLocalList = getFilteredApps(mAppList, filterOption); + generateFilterListLocked(getFilteredApps(mAppList, filterOption, mCurrentFilterMap!= null, mCurrentFilterMap)); // Check for all properties in map before sorting. Populate values from cache for(ApplicationInfo applicationInfo : mAppLocalList) { AppInfo appInfo = mCache.getEntry(applicationInfo.packageName); @@ -1220,8 +1292,9 @@ public class ManageApplications extends ListActivity implements mAppList.add(info); // Add entry to map. Note that the index gets adjusted later on based on // whether the newly added package is part of displayed list + CharSequence label = info.loadLabel(mPm); mCache.addEntry(new AppInfo(pkgName, -1, - info.loadLabel(mPm), info.loadIcon(mPm), size, formattedSize)); + label, info.loadIcon(mPm), size, formattedSize)); // Add to list if (notInList && (shouldBeInList(mFilterApps, info))) { // Binary search returns a negative index (ie -index) of the position where @@ -1234,7 +1307,7 @@ public class ManageApplications extends ListActivity implements } // New entry newIdx = -newIdx-1; - mAppLocalList.add(newIdx, info); + addFilterListLocked(newIdx, info, label); // Adjust index adjustIndex(); notifyDataSetChanged(); @@ -1286,15 +1359,8 @@ public class ManageApplications extends ListActivity implements if (localLOGV) Log.i(TAG, "Removing " + pkg + " from cache"); mCache.removeEntry(pkg); // Remove from filtered list - int i = 0; - for (ApplicationInfo info : mAppLocalList) { - if (info.packageName.equalsIgnoreCase(pkg)) { - mAppLocalList.remove(i); - if (localLOGV) Log.i(TAG, "Removing " + pkg + " from local list"); - found = true; - break; - } - i++; + if (removeFilterListLocked(pkg)) { + found = true; } } // Adjust indices of list entries @@ -1324,6 +1390,50 @@ public class ManageApplications extends ListActivity implements notifyDataSetChanged(); } } + + public Filter getFilter() { + return mAppFilter; + } + + private class AppFilter extends Filter { + @Override + protected FilterResults performFiltering(CharSequence prefix) { + FilterResults results = new FilterResults(); + if (prefix == null || prefix.length() == 0) { + synchronized (mFilterLock) { + results.values = new HashMap(mFilterMap); + results.count = mFilterMap.size(); + } + } else { + final String prefixString = prefix.toString().toLowerCase(); + Map newMap = new HashMap(); + synchronized (mFilterLock) { + Map localMap = mFilterMap; + Set keys = mFilterMap.keySet(); + for (String key : keys) { + String label = localMap.get(key); + if (label.indexOf(prefixString) != -1) { + newMap.put(key, label); + } + } + } + results.values = newMap; + results.count = newMap.size(); + } + return results; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + mCurrentFilterMap = (Map) results.values; + reverseGenerateList(); + if (results.count > 0) { + notifyDataSetChanged(); + } else { + notifyDataSetInvalidated(); + } + } + } } /* @@ -1481,6 +1591,7 @@ public class ManageApplications extends ListActivity implements lv.setSaveEnabled(true); lv.setItemsCanFocus(true); lv.setOnItemClickListener(this); + lv.setTextFilterEnabled(true); mListView = lv; if (DEBUG_TIME) { Log.i(TAG, "Total time in Activity.create:: " +