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