Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.applications;
     18 
     19 import static com.android.settings.Utils.prepareCustomPreferencesList;
     20 
     21 import android.app.Activity;
     22 import android.app.Fragment;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.pm.ApplicationInfo;
     26 import android.content.pm.IPackageManager;
     27 import android.content.pm.PackageInfo;
     28 import android.os.Bundle;
     29 import android.os.Environment;
     30 import android.os.RemoteException;
     31 import android.os.ServiceManager;
     32 import android.os.StatFs;
     33 import android.preference.PreferenceActivity;
     34 import android.provider.Settings;
     35 import android.text.format.Formatter;
     36 import android.util.Log;
     37 import android.view.LayoutInflater;
     38 import android.view.Menu;
     39 import android.view.MenuInflater;
     40 import android.view.MenuItem;
     41 import android.view.View;
     42 import android.view.ViewGroup;
     43 import android.view.animation.AnimationUtils;
     44 import android.view.inputmethod.InputMethodManager;
     45 import android.widget.AbsListView;
     46 import android.widget.AdapterView;
     47 import android.widget.AdapterView.OnItemClickListener;
     48 import android.widget.BaseAdapter;
     49 import android.widget.CheckBox;
     50 import android.widget.Filter;
     51 import android.widget.Filterable;
     52 import android.widget.ImageView;
     53 import android.widget.ListView;
     54 import android.widget.TabHost;
     55 import android.widget.TextView;
     56 
     57 import com.android.internal.content.PackageHelper;
     58 import com.android.settings.R;
     59 import com.android.settings.Settings.RunningServicesActivity;
     60 import com.android.settings.Settings.StorageUseActivity;
     61 import com.android.settings.applications.ApplicationsState.AppEntry;
     62 
     63 import java.util.ArrayList;
     64 import java.util.Comparator;
     65 
     66 final class CanBeOnSdCardChecker {
     67     final IPackageManager mPm;
     68     int mInstallLocation;
     69 
     70     CanBeOnSdCardChecker() {
     71         mPm = IPackageManager.Stub.asInterface(
     72                 ServiceManager.getService("package"));
     73     }
     74 
     75     void init() {
     76         try {
     77             mInstallLocation = mPm.getInstallLocation();
     78         } catch (RemoteException e) {
     79             Log.e("CanBeOnSdCardChecker", "Is Package Manager running?");
     80             return;
     81         }
     82     }
     83 
     84     boolean check(ApplicationInfo info) {
     85         boolean canBe = false;
     86         if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
     87             canBe = true;
     88         } else {
     89             if ((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0 &&
     90                     (info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
     91                 if (info.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL ||
     92                         info.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
     93                     canBe = true;
     94                 } else if (info.installLocation
     95                         == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
     96                     if (mInstallLocation == PackageHelper.APP_INSTALL_EXTERNAL) {
     97                         // For apps with no preference and the default value set
     98                         // to install on sdcard.
     99                         canBe = true;
    100                     }
    101                 }
    102             }
    103         }
    104         return canBe;
    105     }
    106 }
    107 
    108 /**
    109  * Activity to pick an application that will be used to display installation information and
    110  * options to uninstall/delete user data for system applications. This activity
    111  * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
    112  * intent.
    113  */
    114 public class ManageApplications extends Fragment implements
    115         OnItemClickListener,
    116         TabHost.TabContentFactory, TabHost.OnTabChangeListener {
    117     static final String TAG = "ManageApplications";
    118     static final boolean DEBUG = false;
    119 
    120     // attributes used as keys when passing values to InstalledAppDetails activity
    121     public static final String APP_CHG = "chg";
    122 
    123     // constant value that can be used to check return code from sub activity.
    124     private static final int INSTALLED_APP_DETAILS = 1;
    125 
    126     public static final int SIZE_TOTAL = 0;
    127     public static final int SIZE_INTERNAL = 1;
    128     public static final int SIZE_EXTERNAL = 2;
    129 
    130     // sort order that can be changed through the menu can be sorted alphabetically
    131     // or size(descending)
    132     private static final int MENU_OPTIONS_BASE = 0;
    133     // Filter options used for displayed list of applications
    134     public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0;
    135     public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1;
    136     public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2;
    137 
    138     public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4;
    139     public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5;
    140     public static final int SHOW_RUNNING_SERVICES = MENU_OPTIONS_BASE + 6;
    141     public static final int SHOW_BACKGROUND_PROCESSES = MENU_OPTIONS_BASE + 7;
    142     // sort order
    143     private int mSortOrder = SORT_ORDER_ALPHA;
    144     // Filter value
    145     private int mFilterApps = FILTER_APPS_THIRD_PARTY;
    146 
    147     private ApplicationsState mApplicationsState;
    148     private ApplicationsAdapter mApplicationsAdapter;
    149 
    150     // Size resource used for packages whose size computation failed for some reason
    151     private CharSequence mInvalidSizeStr;
    152     private CharSequence mComputingSizeStr;
    153 
    154     // layout inflater object used to inflate views
    155     private LayoutInflater mInflater;
    156 
    157     private String mCurrentPkgName;
    158 
    159     private View mLoadingContainer;
    160 
    161     private View mListContainer;
    162 
    163     // ListView used to display list
    164     private ListView mListView;
    165     // Custom view used to display running processes
    166     private RunningProcessesView mRunningProcessesView;
    167 
    168     LinearColorBar mColorBar;
    169     TextView mStorageChartLabel;
    170     TextView mUsedStorageText;
    171     TextView mFreeStorageText;
    172 
    173     private Menu mOptionsMenu;
    174 
    175     // These are for keeping track of activity and tab switch state.
    176     private int mCurView;
    177     private boolean mCreatedRunning;
    178 
    179     private boolean mResumedRunning;
    180     private boolean mActivityResumed;
    181 
    182     private StatFs mDataFileStats;
    183     private StatFs mSDCardFileStats;
    184     private boolean mLastShowedInternalStorage = true;
    185     private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage;
    186 
    187     static final String TAB_DOWNLOADED = "Downloaded";
    188     static final String TAB_RUNNING = "Running";
    189     static final String TAB_ALL = "All";
    190     static final String TAB_SDCARD = "OnSdCard";
    191     private View mRootView;
    192 
    193     private boolean mShowBackground = false;
    194 
    195     // -------------- Copied from TabActivity --------------
    196 
    197     private TabHost mTabHost;
    198     private String mDefaultTab = null;
    199 
    200     // -------------- Copied from TabActivity --------------
    201 
    202     final Runnable mRunningProcessesAvail = new Runnable() {
    203         public void run() {
    204             handleRunningProcessesAvail();
    205         }
    206     };
    207 
    208     // View Holder used when displaying views
    209     static class AppViewHolder {
    210         ApplicationsState.AppEntry entry;
    211         TextView appName;
    212         ImageView appIcon;
    213         TextView appSize;
    214         TextView disabled;
    215         CheckBox checkBox;
    216 
    217         void updateSizeText(ManageApplications ma, int whichSize) {
    218             if (DEBUG) Log.i(TAG, "updateSizeText of " + entry.label + " " + entry
    219                     + ": " + entry.sizeStr);
    220             if (entry.sizeStr != null) {
    221                 switch (whichSize) {
    222                     case SIZE_INTERNAL:
    223                         appSize.setText(entry.internalSizeStr);
    224                         break;
    225                     case SIZE_EXTERNAL:
    226                         appSize.setText(entry.externalSizeStr);
    227                         break;
    228                     default:
    229                         appSize.setText(entry.sizeStr);
    230                         break;
    231                 }
    232             } else if (entry.size == ApplicationsState.SIZE_INVALID) {
    233                 appSize.setText(ma.mInvalidSizeStr);
    234             }
    235         }
    236     }
    237 
    238     /*
    239      * Custom adapter implementation for the ListView
    240      * This adapter maintains a map for each displayed application and its properties
    241      * An index value on each AppInfo object indicates the correct position or index
    242      * in the list. If the list gets updated dynamically when the user is viewing the list of
    243      * applications, we need to return the correct index of position. This is done by mapping
    244      * the getId methods via the package name into the internal maps and indices.
    245      * The order of applications in the list is mirrored in mAppLocalList
    246      */
    247     class ApplicationsAdapter extends BaseAdapter implements Filterable,
    248             ApplicationsState.Callbacks, AbsListView.RecyclerListener {
    249         private final ApplicationsState mState;
    250         private final ArrayList<View> mActive = new ArrayList<View>();
    251         private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
    252         private ArrayList<ApplicationsState.AppEntry> mEntries;
    253         private boolean mResumed;
    254         private int mLastFilterMode=-1, mLastSortMode=-1;
    255         private boolean mWaitingForData;
    256         private int mWhichSize = SIZE_TOTAL;
    257         CharSequence mCurFilterPrefix;
    258 
    259         private Filter mFilter = new Filter() {
    260             @Override
    261             protected FilterResults performFiltering(CharSequence constraint) {
    262                 ArrayList<ApplicationsState.AppEntry> entries
    263                         = applyPrefixFilter(constraint, mBaseEntries);
    264                 FilterResults fr = new FilterResults();
    265                 fr.values = entries;
    266                 fr.count = entries.size();
    267                 return fr;
    268             }
    269 
    270             @Override
    271             protected void publishResults(CharSequence constraint, FilterResults results) {
    272                 mCurFilterPrefix = constraint;
    273                 mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values;
    274                 notifyDataSetChanged();
    275                 updateStorageUsage();
    276             }
    277         };
    278 
    279         public ApplicationsAdapter(ApplicationsState state) {
    280             mState = state;
    281         }
    282 
    283         public void resume(int filter, int sort) {
    284             if (DEBUG) Log.i(TAG, "Resume!  mResumed=" + mResumed);
    285             if (!mResumed) {
    286                 mResumed = true;
    287                 mState.resume(this);
    288                 mLastFilterMode = filter;
    289                 mLastSortMode = sort;
    290                 rebuild(true);
    291             } else {
    292                 rebuild(filter, sort);
    293             }
    294         }
    295 
    296         public void pause() {
    297             if (mResumed) {
    298                 mResumed = false;
    299                 mState.pause();
    300             }
    301         }
    302 
    303         public void rebuild(int filter, int sort) {
    304             if (filter == mLastFilterMode && sort == mLastSortMode) {
    305                 return;
    306             }
    307             mLastFilterMode = filter;
    308             mLastSortMode = sort;
    309             rebuild(true);
    310         }
    311 
    312         public void rebuild(boolean eraseold) {
    313             if (DEBUG) Log.i(TAG, "Rebuilding app list...");
    314             ApplicationsState.AppFilter filterObj;
    315             Comparator<AppEntry> comparatorObj;
    316             boolean emulated = Environment.isExternalStorageEmulated();
    317             if (emulated) {
    318                 mWhichSize = SIZE_TOTAL;
    319             } else {
    320                 mWhichSize = SIZE_INTERNAL;
    321             }
    322             switch (mLastFilterMode) {
    323                 case FILTER_APPS_THIRD_PARTY:
    324                     filterObj = ApplicationsState.THIRD_PARTY_FILTER;
    325                     break;
    326                 case FILTER_APPS_SDCARD:
    327                     filterObj = ApplicationsState.ON_SD_CARD_FILTER;
    328                     if (!emulated) {
    329                         mWhichSize = SIZE_EXTERNAL;
    330                     }
    331                     break;
    332                 default:
    333                     filterObj = null;
    334                     break;
    335             }
    336             switch (mLastSortMode) {
    337                 case SORT_ORDER_SIZE:
    338                     switch (mWhichSize) {
    339                         case SIZE_INTERNAL:
    340                             comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
    341                             break;
    342                         case SIZE_EXTERNAL:
    343                             comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
    344                             break;
    345                         default:
    346                             comparatorObj = ApplicationsState.SIZE_COMPARATOR;
    347                             break;
    348                     }
    349                     break;
    350                 default:
    351                     comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
    352                     break;
    353             }
    354             ArrayList<ApplicationsState.AppEntry> entries
    355                     = mState.rebuild(filterObj, comparatorObj);
    356             if (entries == null && !eraseold) {
    357                 // Don't have new list yet, but can continue using the old one.
    358                 return;
    359             }
    360             mBaseEntries = entries;
    361             if (mBaseEntries != null) {
    362                 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
    363             } else {
    364                 mEntries = null;
    365             }
    366             notifyDataSetChanged();
    367             updateStorageUsage();
    368 
    369             if (entries == null) {
    370                 mWaitingForData = true;
    371                 mListContainer.setVisibility(View.INVISIBLE);
    372                 mLoadingContainer.setVisibility(View.VISIBLE);
    373             } else {
    374                 mListContainer.setVisibility(View.VISIBLE);
    375                 mLoadingContainer.setVisibility(View.GONE);
    376             }
    377         }
    378 
    379         ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
    380                 ArrayList<ApplicationsState.AppEntry> origEntries) {
    381             if (prefix == null || prefix.length() == 0) {
    382                 return origEntries;
    383             } else {
    384                 String prefixStr = ApplicationsState.normalize(prefix.toString());
    385                 final String spacePrefixStr = " " + prefixStr;
    386                 ArrayList<ApplicationsState.AppEntry> newEntries
    387                         = new ArrayList<ApplicationsState.AppEntry>();
    388                 for (int i=0; i<origEntries.size(); i++) {
    389                     ApplicationsState.AppEntry entry = origEntries.get(i);
    390                     String nlabel = entry.getNormalizedLabel();
    391                     if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
    392                         newEntries.add(entry);
    393                     }
    394                 }
    395                 return newEntries;
    396             }
    397         }
    398 
    399         @Override
    400         public void onRunningStateChanged(boolean running) {
    401             getActivity().setProgressBarIndeterminateVisibility(running);
    402         }
    403 
    404         @Override
    405         public void onRebuildComplete(ArrayList<AppEntry> apps) {
    406             if (mLoadingContainer.getVisibility() == View.VISIBLE) {
    407                 mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
    408                         getActivity(), android.R.anim.fade_out));
    409                 mListContainer.startAnimation(AnimationUtils.loadAnimation(
    410                         getActivity(), android.R.anim.fade_in));
    411             }
    412             mListContainer.setVisibility(View.VISIBLE);
    413             mLoadingContainer.setVisibility(View.GONE);
    414             mWaitingForData = false;
    415             mBaseEntries = apps;
    416             mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
    417             notifyDataSetChanged();
    418             updateStorageUsage();
    419         }
    420 
    421         @Override
    422         public void onPackageListChanged() {
    423             rebuild(false);
    424         }
    425 
    426         @Override
    427         public void onPackageIconChanged() {
    428             // We ensure icons are loaded when their item is displayed, so
    429             // don't care about icons loaded in the background.
    430         }
    431 
    432         @Override
    433         public void onPackageSizeChanged(String packageName) {
    434             for (int i=0; i<mActive.size(); i++) {
    435                 AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
    436                 if (holder.entry.info.packageName.equals(packageName)) {
    437                     synchronized (holder.entry) {
    438                         holder.updateSizeText(ManageApplications.this, mWhichSize);
    439                     }
    440                     if (holder.entry.info.packageName.equals(mCurrentPkgName)
    441                             && mLastSortMode == SORT_ORDER_SIZE) {
    442                         // We got the size information for the last app the
    443                         // user viewed, and are sorting by size...  they may
    444                         // have cleared data, so we immediately want to resort
    445                         // the list with the new size to reflect it to the user.
    446                         rebuild(false);
    447                     }
    448                     updateStorageUsage();
    449                     return;
    450                 }
    451             }
    452         }
    453 
    454         @Override
    455         public void onAllSizesComputed() {
    456             if (mLastSortMode == SORT_ORDER_SIZE) {
    457                 rebuild(false);
    458             }
    459         }
    460 
    461         public int getCount() {
    462             return mEntries != null ? mEntries.size() : 0;
    463         }
    464 
    465         public Object getItem(int position) {
    466             return mEntries.get(position);
    467         }
    468 
    469         public ApplicationsState.AppEntry getAppEntry(int position) {
    470             return mEntries.get(position);
    471         }
    472 
    473         public long getItemId(int position) {
    474             return mEntries.get(position).id;
    475         }
    476 
    477         public View getView(int position, View convertView, ViewGroup parent) {
    478             // A ViewHolder keeps references to children views to avoid unnecessary calls
    479             // to findViewById() on each row.
    480             AppViewHolder holder;
    481 
    482             // When convertView is not null, we can reuse it directly, there is no need
    483             // to reinflate it. We only inflate a new View when the convertView supplied
    484             // by ListView is null.
    485             if (convertView == null) {
    486                 convertView = mInflater.inflate(R.layout.manage_applications_item, null);
    487 
    488                 // Creates a ViewHolder and store references to the two children views
    489                 // we want to bind data to.
    490                 holder = new AppViewHolder();
    491                 holder.appName = (TextView) convertView.findViewById(R.id.app_name);
    492                 holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
    493                 holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
    494                 holder.disabled = (TextView) convertView.findViewById(R.id.app_disabled);
    495                 holder.checkBox = (CheckBox) convertView.findViewById(R.id.app_on_sdcard);
    496                 convertView.setTag(holder);
    497             } else {
    498                 // Get the ViewHolder back to get fast access to the TextView
    499                 // and the ImageView.
    500                 holder = (AppViewHolder) convertView.getTag();
    501             }
    502 
    503             // Bind the data efficiently with the holder
    504             ApplicationsState.AppEntry entry = mEntries.get(position);
    505             synchronized (entry) {
    506                 holder.entry = entry;
    507                 if (entry.label != null) {
    508                     holder.appName.setText(entry.label);
    509                     holder.appName.setTextColor(getActivity().getResources().getColorStateList(
    510                             entry.info.enabled ? android.R.color.primary_text_dark
    511                                     : android.R.color.secondary_text_dark));
    512                 }
    513                 mState.ensureIcon(entry);
    514                 if (entry.icon != null) {
    515                     holder.appIcon.setImageDrawable(entry.icon);
    516                 }
    517                 holder.updateSizeText(ManageApplications.this, mWhichSize);
    518                 if (InstalledAppDetails.SUPPORT_DISABLE_APPS) {
    519                     holder.disabled.setVisibility(entry.info.enabled ? View.GONE : View.VISIBLE);
    520                 } else {
    521                     holder.disabled.setVisibility(View.GONE);
    522                 }
    523                 if (mLastFilterMode == FILTER_APPS_SDCARD) {
    524                     holder.checkBox.setVisibility(View.VISIBLE);
    525                     holder.checkBox.setChecked((entry.info.flags
    526                             & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
    527                 } else {
    528                     holder.checkBox.setVisibility(View.GONE);
    529                 }
    530             }
    531             mActive.remove(convertView);
    532             mActive.add(convertView);
    533             return convertView;
    534         }
    535 
    536         @Override
    537         public Filter getFilter() {
    538             return mFilter;
    539         }
    540 
    541         @Override
    542         public void onMovedToScrapHeap(View view) {
    543             mActive.remove(view);
    544         }
    545     }
    546 
    547     @Override
    548     public void onCreate(Bundle savedInstanceState) {
    549         super.onCreate(savedInstanceState);
    550 
    551         setHasOptionsMenu(true);
    552 
    553         mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
    554         mApplicationsAdapter = new ApplicationsAdapter(mApplicationsState);
    555         Intent intent = getActivity().getIntent();
    556         String action = intent.getAction();
    557         String defaultTabTag = TAB_DOWNLOADED;
    558         String className = getArguments() != null
    559                 ? getArguments().getString("classname") : null;
    560         if (className == null) {
    561             className = intent.getComponent().getClassName();
    562         }
    563         if (className.equals(RunningServicesActivity.class.getName())
    564                 || className.endsWith(".RunningServices")) {
    565             defaultTabTag = TAB_RUNNING;
    566         } else if (className.equals(StorageUseActivity.class.getName())
    567                 || Intent.ACTION_MANAGE_PACKAGE_STORAGE.equals(action)
    568                 || className.endsWith(".StorageUse")) {
    569             mSortOrder = SORT_ORDER_SIZE;
    570             mFilterApps = FILTER_APPS_ALL;
    571             defaultTabTag = TAB_ALL;
    572         } else if (Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) {
    573             // Select the all-apps tab, with the default sorting
    574             defaultTabTag = TAB_ALL;
    575         }
    576 
    577         if (savedInstanceState != null) {
    578             mSortOrder = savedInstanceState.getInt("sortOrder", mSortOrder);
    579             mFilterApps = savedInstanceState.getInt("filterApps", mFilterApps);
    580             String tmp = savedInstanceState.getString("defaultTabTag");
    581             if (tmp != null) defaultTabTag = tmp;
    582             mShowBackground = savedInstanceState.getBoolean("showBackground", false);
    583         }
    584 
    585         mDefaultTab = defaultTabTag;
    586 
    587         mDataFileStats = new StatFs("/data");
    588         mSDCardFileStats = new StatFs(Environment.getExternalStorageDirectory().toString());
    589 
    590         mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
    591         mComputingSizeStr = getActivity().getText(R.string.computing_size);
    592     }
    593 
    594 
    595     @Override
    596     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    597         // initialize the inflater
    598         mInflater = inflater;
    599         mRootView = inflater.inflate(R.layout.manage_applications, null);
    600         mLoadingContainer = mRootView.findViewById(R.id.loading_container);
    601         mListContainer = mRootView.findViewById(R.id.list_container);
    602         // Create adapter and list view here
    603         ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
    604         View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
    605         if (emptyView != null) {
    606             lv.setEmptyView(emptyView);
    607         }
    608         lv.setOnItemClickListener(this);
    609         lv.setSaveEnabled(true);
    610         lv.setItemsCanFocus(true);
    611         lv.setOnItemClickListener(this);
    612         lv.setTextFilterEnabled(true);
    613         mListView = lv;
    614         lv.setRecyclerListener(mApplicationsAdapter);
    615         mListView.setAdapter(mApplicationsAdapter);
    616         mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar);
    617         mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel);
    618         mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText);
    619         mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText);
    620         mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
    621                 R.id.running_processes);
    622 
    623         mCreatedRunning = mResumedRunning = false;
    624         mCurView = VIEW_NOTHING;
    625 
    626         mTabHost = (TabHost) mInflater.inflate(R.layout.manage_apps_tab_content, container, false);
    627         mTabHost.setup();
    628         final TabHost tabHost = mTabHost;
    629         tabHost.addTab(tabHost.newTabSpec(TAB_DOWNLOADED)
    630                 .setIndicator(getActivity().getString(R.string.filter_apps_third_party),
    631                         getActivity().getResources().getDrawable(R.drawable.ic_tab_download))
    632                 .setContent(this));
    633         if (!Environment.isExternalStorageEmulated()) {
    634             tabHost.addTab(tabHost.newTabSpec(TAB_SDCARD)
    635                     .setIndicator(getActivity().getString(R.string.filter_apps_onsdcard),
    636                             getActivity().getResources().getDrawable(R.drawable.ic_tab_sdcard))
    637                     .setContent(this));
    638         }
    639         tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING)
    640                 .setIndicator(getActivity().getString(R.string.filter_apps_running),
    641                         getActivity().getResources().getDrawable(R.drawable.ic_tab_running))
    642                 .setContent(this));
    643         tabHost.addTab(tabHost.newTabSpec(TAB_ALL)
    644                 .setIndicator(getActivity().getString(R.string.filter_apps_all),
    645                         getActivity().getResources().getDrawable(R.drawable.ic_tab_all))
    646                 .setContent(this));
    647         tabHost.setCurrentTabByTag(mDefaultTab);
    648         tabHost.setOnTabChangedListener(this);
    649 
    650         // adjust padding around tabwidget as needed
    651         prepareCustomPreferencesList(container, mTabHost, mListView, false);
    652 
    653         return mTabHost;
    654     }
    655 
    656     @Override
    657     public void onStart() {
    658         super.onStart();
    659     }
    660 
    661     @Override
    662     public void onResume() {
    663         super.onResume();
    664         mActivityResumed = true;
    665         showCurrentTab();
    666         updateOptionsMenu();
    667         mTabHost.getTabWidget().setEnabled(true);
    668     }
    669 
    670     @Override
    671     public void onSaveInstanceState(Bundle outState) {
    672         super.onSaveInstanceState(outState);
    673         outState.putInt("sortOrder", mSortOrder);
    674         outState.putInt("filterApps", mFilterApps);
    675         if (mDefaultTab != null) {
    676             outState.putString("defautTabTag", mDefaultTab);
    677         }
    678         outState.putBoolean("showBackground", mShowBackground);
    679     }
    680 
    681     @Override
    682     public void onPause() {
    683         super.onPause();
    684         mActivityResumed = false;
    685         mApplicationsAdapter.pause();
    686         if (mResumedRunning) {
    687             mRunningProcessesView.doPause();
    688             mResumedRunning = false;
    689         }
    690         mTabHost.getTabWidget().setEnabled(false);
    691     }
    692 
    693     @Override
    694     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    695         if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
    696             mApplicationsState.requestSize(mCurrentPkgName);
    697         }
    698     }
    699 
    700     // utility method used to start sub activity
    701     private void startApplicationDetailsActivity() {
    702         // start new fragment to display extended information
    703         Bundle args = new Bundle();
    704         args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mCurrentPkgName);
    705 
    706         PreferenceActivity pa = (PreferenceActivity)getActivity();
    707         pa.startPreferencePanel(InstalledAppDetails.class.getName(), args,
    708                 R.string.application_info_label, null, this, INSTALLED_APP_DETAILS);
    709     }
    710 
    711     @Override
    712     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    713         Log.i(TAG, "onCreateOptionsMenu in " + this + ": " + menu);
    714         mOptionsMenu = menu;
    715         // note: icons removed for now because the cause the new action
    716         // bar UI to be very confusing.
    717         menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
    718                 //.setIcon(android.R.drawable.ic_menu_sort_alphabetically)
    719                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    720         menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
    721                 //.setIcon(android.R.drawable.ic_menu_sort_by_size)
    722                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    723         menu.add(0, SHOW_RUNNING_SERVICES, 3, R.string.show_running_services)
    724                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    725         menu.add(0, SHOW_BACKGROUND_PROCESSES, 3, R.string.show_background_processes)
    726                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    727         updateOptionsMenu();
    728     }
    729 
    730     @Override
    731     public void onPrepareOptionsMenu(Menu menu) {
    732         updateOptionsMenu();
    733     }
    734 
    735     @Override
    736     public void onDestroyOptionsMenu() {
    737         mOptionsMenu = null;
    738     }
    739 
    740     void updateOptionsMenu() {
    741         if (mOptionsMenu == null) {
    742             return;
    743         }
    744 
    745         /*
    746          * The running processes screen doesn't use the mApplicationsAdapter
    747          * so bringing up this menu in that case doesn't make any sense.
    748          */
    749         if (mCurView == VIEW_RUNNING) {
    750             boolean showingBackground = mRunningProcessesView != null
    751                     ? mRunningProcessesView.mAdapter.getShowBackground() : false;
    752             mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(false);
    753             mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(false);
    754             mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground);
    755             mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground);
    756         } else {
    757             mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
    758             mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
    759             mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(false);
    760             mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(false);
    761         }
    762     }
    763 
    764     @Override
    765     public boolean onOptionsItemSelected(MenuItem item) {
    766         int menuId = item.getItemId();
    767         if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
    768             mSortOrder = menuId;
    769             if (mCurView != VIEW_RUNNING) {
    770                 mApplicationsAdapter.rebuild(mFilterApps, mSortOrder);
    771             }
    772         } else if (menuId == SHOW_RUNNING_SERVICES) {
    773             mShowBackground = false;
    774             mRunningProcessesView.mAdapter.setShowBackground(false);
    775         } else if (menuId == SHOW_BACKGROUND_PROCESSES) {
    776             mShowBackground = true;
    777             mRunningProcessesView.mAdapter.setShowBackground(true);
    778         }
    779         updateOptionsMenu();
    780         return true;
    781     }
    782 
    783     public void onItemClick(AdapterView<?> parent, View view, int position,
    784             long id) {
    785         ApplicationsState.AppEntry entry = mApplicationsAdapter.getAppEntry(position);
    786         mCurrentPkgName = entry.info.packageName;
    787         startApplicationDetailsActivity();
    788     }
    789 
    790     public View createTabContent(String tag) {
    791         return mRootView;
    792     }
    793 
    794     static final int VIEW_NOTHING = 0;
    795     static final int VIEW_LIST = 1;
    796     static final int VIEW_RUNNING = 2;
    797 
    798     void updateStorageUsage() {
    799         if (mCurView == VIEW_RUNNING) {
    800             return;
    801         }
    802 
    803         long freeStorage = 0;
    804         long appStorage = 0;
    805         long totalStorage = 0;
    806         CharSequence newLabel = null;
    807 
    808         if (mFilterApps == FILTER_APPS_SDCARD) {
    809             if (mLastShowedInternalStorage) {
    810                 mLastShowedInternalStorage = false;
    811             }
    812             newLabel = getActivity().getText(R.string.sd_card_storage);
    813             mSDCardFileStats.restat(Environment.getExternalStorageDirectory().toString());
    814             try {
    815                 totalStorage = (long)mSDCardFileStats.getBlockCount() *
    816                         mSDCardFileStats.getBlockSize();
    817                 freeStorage = (long) mSDCardFileStats.getAvailableBlocks() *
    818                 mSDCardFileStats.getBlockSize();
    819             } catch (IllegalArgumentException e) {
    820                 // use the old value of mFreeMem
    821             }
    822             final int N = mApplicationsAdapter.getCount();
    823             for (int i=0; i<N; i++) {
    824                 ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i);
    825                 appStorage += ae.externalCodeSize + ae.externalDataSize;
    826             }
    827         } else {
    828             if (!mLastShowedInternalStorage) {
    829                 mLastShowedInternalStorage = true;
    830             }
    831             newLabel = getActivity().getText(R.string.internal_storage);
    832             mDataFileStats.restat("/data");
    833             try {
    834                 totalStorage = (long)mDataFileStats.getBlockCount() *
    835                         mDataFileStats.getBlockSize();
    836                 freeStorage = (long) mDataFileStats.getAvailableBlocks() *
    837                     mDataFileStats.getBlockSize();
    838             } catch (IllegalArgumentException e) {
    839             }
    840             final boolean emulatedStorage = Environment.isExternalStorageEmulated();
    841             final int N = mApplicationsAdapter.getCount();
    842             for (int i=0; i<N; i++) {
    843                 ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i);
    844                 appStorage += ae.codeSize + ae.dataSize;
    845                 if (emulatedStorage) {
    846                     appStorage += ae.externalCodeSize + ae.externalDataSize;
    847                 }
    848             }
    849             freeStorage += mApplicationsState.sumCacheSizes();
    850         }
    851         if (newLabel != null) {
    852             mStorageChartLabel.setText(newLabel);
    853         }
    854         if (totalStorage > 0) {
    855             mColorBar.setRatios((totalStorage-freeStorage-appStorage)/(float)totalStorage,
    856                     appStorage/(float)totalStorage, freeStorage/(float)totalStorage);
    857             long usedStorage = totalStorage - freeStorage;
    858             if (mLastUsedStorage != usedStorage) {
    859                 mLastUsedStorage = usedStorage;
    860                 String sizeStr = Formatter.formatShortFileSize(getActivity(), usedStorage);
    861                 mUsedStorageText.setText(getActivity().getResources().getString(
    862                         R.string.service_foreground_processes, sizeStr));
    863             }
    864             if (mLastFreeStorage != freeStorage) {
    865                 mLastFreeStorage = freeStorage;
    866                 String sizeStr = Formatter.formatShortFileSize(getActivity(), freeStorage);
    867                 mFreeStorageText.setText(getActivity().getResources().getString(
    868                         R.string.service_background_processes, sizeStr));
    869             }
    870         } else {
    871             mColorBar.setRatios(0, 0, 0);
    872             if (mLastUsedStorage != -1) {
    873                 mLastUsedStorage = -1;
    874                 mUsedStorageText.setText("");
    875             }
    876             if (mLastFreeStorage != -1) {
    877                 mLastFreeStorage = -1;
    878                 mFreeStorageText.setText("");
    879             }
    880         }
    881     }
    882 
    883     private void selectView(int which) {
    884         if (which == VIEW_LIST) {
    885             if (mResumedRunning) {
    886                 mRunningProcessesView.doPause();
    887                 mResumedRunning = false;
    888             }
    889             if (mCurView != which) {
    890                 mRunningProcessesView.setVisibility(View.GONE);
    891                 mListContainer.setVisibility(View.VISIBLE);
    892                 mLoadingContainer.setVisibility(View.GONE);
    893             }
    894             if (mActivityResumed) {
    895                 mApplicationsAdapter.resume(mFilterApps, mSortOrder);
    896             }
    897         } else if (which == VIEW_RUNNING) {
    898             if (!mCreatedRunning) {
    899                 mRunningProcessesView.doCreate(null);
    900                 mRunningProcessesView.mAdapter.setShowBackground(mShowBackground);
    901                 mCreatedRunning = true;
    902             }
    903             boolean haveData = true;
    904             if (mActivityResumed && !mResumedRunning) {
    905                 haveData = mRunningProcessesView.doResume(this, mRunningProcessesAvail);
    906                 mResumedRunning = true;
    907             }
    908             mApplicationsAdapter.pause();
    909             if (mCurView != which) {
    910                 if (haveData) {
    911                     mRunningProcessesView.setVisibility(View.VISIBLE);
    912                 } else {
    913                     mLoadingContainer.setVisibility(View.VISIBLE);
    914                 }
    915                 mListContainer.setVisibility(View.GONE);
    916             }
    917         }
    918         mCurView = which;
    919         final Activity host = getActivity();
    920         if (host != null) {
    921             host.invalidateOptionsMenu();
    922         }
    923     }
    924 
    925     void handleRunningProcessesAvail() {
    926         if (mCurView == VIEW_RUNNING) {
    927             mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
    928                     getActivity(), android.R.anim.fade_out));
    929             mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation(
    930                     getActivity(), android.R.anim.fade_in));
    931             mRunningProcessesView.setVisibility(View.VISIBLE);
    932             mLoadingContainer.setVisibility(View.GONE);
    933         }
    934     }
    935 
    936     public void showCurrentTab() {
    937         String tabId = mDefaultTab = mTabHost.getCurrentTabTag();
    938         int newOption;
    939         if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) {
    940             newOption = FILTER_APPS_THIRD_PARTY;
    941         } else if (TAB_ALL.equalsIgnoreCase(tabId)) {
    942             newOption = FILTER_APPS_ALL;
    943         } else if (TAB_SDCARD.equalsIgnoreCase(tabId)) {
    944             newOption = FILTER_APPS_SDCARD;
    945         } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
    946             ((InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE))
    947                     .hideSoftInputFromWindow(
    948                             getActivity().getWindow().getDecorView().getWindowToken(), 0);
    949             selectView(VIEW_RUNNING);
    950             return;
    951         } else {
    952             // Invalid option. Do nothing
    953             return;
    954         }
    955 
    956         mFilterApps = newOption;
    957         selectView(VIEW_LIST);
    958         updateStorageUsage();
    959         updateOptionsMenu();
    960     }
    961 
    962     public void onTabChanged(String tabId) {
    963         showCurrentTab();
    964     }
    965 }
    966