Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2015 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 android.app.ActivityManager;
     20 import android.app.AlertDialog;
     21 import android.app.AppGlobals;
     22 import android.content.Context;
     23 import android.content.DialogInterface;
     24 import android.content.Intent;
     25 import android.content.UriPermission;
     26 import android.content.pm.ApplicationInfo;
     27 import android.content.pm.IPackageDataObserver;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.ProviderInfo;
     30 import android.os.Bundle;
     31 import android.os.Environment;
     32 import android.os.Handler;
     33 import android.os.Message;
     34 import android.os.RemoteException;
     35 import android.os.UserHandle;
     36 import android.os.storage.StorageManager;
     37 import android.os.storage.VolumeInfo;
     38 import android.support.v7.preference.Preference;
     39 import android.support.v7.preference.PreferenceCategory;
     40 import android.text.format.Formatter;
     41 import android.util.Log;
     42 import android.util.MutableInt;
     43 import android.view.View;
     44 import android.view.View.OnClickListener;
     45 import android.widget.Button;
     46 
     47 import com.android.internal.logging.MetricsProto.MetricsEvent;
     48 import com.android.settings.R;
     49 import com.android.settings.Utils;
     50 import com.android.settings.deviceinfo.StorageWizardMoveConfirm;
     51 import com.android.settingslib.RestrictedLockUtils;
     52 import com.android.settingslib.applications.ApplicationsState;
     53 import com.android.settingslib.applications.ApplicationsState.AppEntry;
     54 import com.android.settingslib.applications.ApplicationsState.Callbacks;
     55 
     56 import java.util.ArrayList;
     57 import java.util.Collections;
     58 import java.util.List;
     59 import java.util.Map;
     60 import java.util.Objects;
     61 import java.util.TreeMap;
     62 
     63 import static android.content.pm.ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
     64 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
     65 
     66 public class AppStorageSettings extends AppInfoWithHeader
     67         implements OnClickListener, Callbacks, DialogInterface.OnClickListener {
     68     private static final String TAG = AppStorageSettings.class.getSimpleName();
     69 
     70     //internal constants used in Handler
     71     private static final int OP_SUCCESSFUL = 1;
     72     private static final int OP_FAILED = 2;
     73     private static final int MSG_CLEAR_USER_DATA = 1;
     74     private static final int MSG_CLEAR_CACHE = 3;
     75 
     76     // invalid size value used initially and also when size retrieval through PackageManager
     77     // fails for whatever reason
     78     private static final int SIZE_INVALID = -1;
     79 
     80     // Result code identifiers
     81     public static final int REQUEST_MANAGE_SPACE = 2;
     82 
     83     private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
     84     private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 2;
     85 
     86     private static final String KEY_STORAGE_USED = "storage_used";
     87     private static final String KEY_CHANGE_STORAGE = "change_storage_button";
     88     private static final String KEY_STORAGE_SPACE = "storage_space";
     89     private static final String KEY_STORAGE_CATEGORY = "storage_category";
     90 
     91     private static final String KEY_TOTAL_SIZE = "total_size";
     92     private static final String KEY_APP_SIZE = "app_size";
     93     private static final String KEY_EXTERNAL_CODE_SIZE = "external_code_size";
     94     private static final String KEY_DATA_SIZE = "data_size";
     95     private static final String KEY_EXTERNAL_DATA_SIZE = "external_data_size";
     96     private static final String KEY_CACHE_SIZE = "cache_size";
     97 
     98     private static final String KEY_CLEAR_DATA = "clear_data_button";
     99     private static final String KEY_CLEAR_CACHE = "clear_cache_button";
    100 
    101     private static final String KEY_URI_CATEGORY = "uri_category";
    102     private static final String KEY_CLEAR_URI = "clear_uri_button";
    103 
    104     private Preference mTotalSize;
    105     private Preference mAppSize;
    106     private Preference mDataSize;
    107     private Preference mExternalCodeSize;
    108     private Preference mExternalDataSize;
    109 
    110     // Views related to cache info
    111     private Preference mCacheSize;
    112     private Button mClearDataButton;
    113     private Button mClearCacheButton;
    114 
    115     private Preference mStorageUsed;
    116     private Button mChangeStorageButton;
    117 
    118     // Views related to URI permissions
    119     private Button mClearUriButton;
    120     private LayoutPreference mClearUri;
    121     private PreferenceCategory mUri;
    122 
    123     private boolean mCanClearData = true;
    124     private boolean mHaveSizes = false;
    125 
    126     private long mLastCodeSize = -1;
    127     private long mLastDataSize = -1;
    128     private long mLastExternalCodeSize = -1;
    129     private long mLastExternalDataSize = -1;
    130     private long mLastCacheSize = -1;
    131     private long mLastTotalSize = -1;
    132 
    133     private ClearCacheObserver mClearCacheObserver;
    134     private ClearUserDataObserver mClearDataObserver;
    135 
    136     // Resource strings
    137     private CharSequence mInvalidSizeStr;
    138     private CharSequence mComputingStr;
    139 
    140     private VolumeInfo[] mCandidates;
    141     private AlertDialog.Builder mDialogBuilder;
    142 
    143     @Override
    144     public void onCreate(Bundle savedInstanceState) {
    145         super.onCreate(savedInstanceState);
    146 
    147         addPreferencesFromResource(R.xml.app_storage_settings);
    148         setupViews();
    149         initMoveDialog();
    150     }
    151 
    152     @Override
    153     public void onResume() {
    154         super.onResume();
    155         mState.requestSize(mPackageName, mUserId);
    156     }
    157 
    158     private void setupViews() {
    159         mComputingStr = getActivity().getText(R.string.computing_size);
    160         mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
    161 
    162         // Set default values on sizes
    163         mTotalSize = findPreference(KEY_TOTAL_SIZE);
    164         mAppSize =  findPreference(KEY_APP_SIZE);
    165         mDataSize =  findPreference(KEY_DATA_SIZE);
    166         mExternalCodeSize = findPreference(KEY_EXTERNAL_CODE_SIZE);
    167         mExternalDataSize = findPreference(KEY_EXTERNAL_DATA_SIZE);
    168 
    169         if (Environment.isExternalStorageEmulated()) {
    170             PreferenceCategory category = (PreferenceCategory) findPreference(KEY_STORAGE_CATEGORY);
    171             category.removePreference(mExternalCodeSize);
    172             category.removePreference(mExternalDataSize);
    173         }
    174         mClearDataButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_DATA))
    175                 .findViewById(R.id.button);
    176 
    177         mStorageUsed = findPreference(KEY_STORAGE_USED);
    178         mChangeStorageButton = (Button) ((LayoutPreference) findPreference(KEY_CHANGE_STORAGE))
    179                 .findViewById(R.id.button);
    180         mChangeStorageButton.setText(R.string.change);
    181         mChangeStorageButton.setOnClickListener(this);
    182 
    183         // Cache section
    184         mCacheSize = findPreference(KEY_CACHE_SIZE);
    185         mClearCacheButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_CACHE))
    186                 .findViewById(R.id.button);
    187         mClearCacheButton.setText(R.string.clear_cache_btn_text);
    188 
    189         // URI permissions section
    190         mUri = (PreferenceCategory) findPreference(KEY_URI_CATEGORY);
    191         mClearUri = (LayoutPreference) mUri.findPreference(KEY_CLEAR_URI);
    192         mClearUriButton = (Button) mClearUri.findViewById(R.id.button);
    193         mClearUriButton.setText(R.string.clear_uri_btn_text);
    194         mClearUriButton.setOnClickListener(this);
    195     }
    196 
    197     @Override
    198     public void onClick(View v) {
    199         if (v == mClearCacheButton) {
    200             if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
    201                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
    202                         getActivity(), mAppsControlDisallowedAdmin);
    203                 return;
    204             } else if (mClearCacheObserver == null) { // Lazy initialization of observer
    205                 mClearCacheObserver = new ClearCacheObserver();
    206             }
    207             mPm.deleteApplicationCacheFiles(mPackageName, mClearCacheObserver);
    208         } else if (v == mClearDataButton) {
    209             if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
    210                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
    211                         getActivity(), mAppsControlDisallowedAdmin);
    212             } else if (mAppEntry.info.manageSpaceActivityName != null) {
    213                 if (!Utils.isMonkeyRunning()) {
    214                     Intent intent = new Intent(Intent.ACTION_DEFAULT);
    215                     intent.setClassName(mAppEntry.info.packageName,
    216                             mAppEntry.info.manageSpaceActivityName);
    217                     startActivityForResult(intent, REQUEST_MANAGE_SPACE);
    218                 }
    219             } else {
    220                 showDialogInner(DLG_CLEAR_DATA, 0);
    221             }
    222         } else if (v == mChangeStorageButton && mDialogBuilder != null && !isMoveInProgress()) {
    223             mDialogBuilder.show();
    224         } else if (v == mClearUriButton) {
    225             if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
    226                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
    227                         getActivity(), mAppsControlDisallowedAdmin);
    228             } else {
    229                 clearUriPermissions();
    230             }
    231         }
    232     }
    233 
    234     private boolean isMoveInProgress() {
    235         try {
    236             // TODO: define a cleaner API for this
    237             AppGlobals.getPackageManager().checkPackageStartable(mPackageName,
    238                     UserHandle.myUserId());
    239             return false;
    240         } catch (RemoteException | SecurityException e) {
    241             return true;
    242         }
    243     }
    244 
    245     @Override
    246     public void onClick(DialogInterface dialog, int which) {
    247         final Context context = getActivity();
    248 
    249         // If not current volume, kick off move wizard
    250         final VolumeInfo targetVol = mCandidates[which];
    251         final VolumeInfo currentVol = context.getPackageManager().getPackageCurrentVolume(
    252                 mAppEntry.info);
    253         if (!Objects.equals(targetVol, currentVol)) {
    254             final Intent intent = new Intent(context, StorageWizardMoveConfirm.class);
    255             intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, targetVol.getId());
    256             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName);
    257             startActivity(intent);
    258         }
    259         dialog.dismiss();
    260     }
    261 
    262     private String getSizeStr(long size) {
    263         if (size == SIZE_INVALID) {
    264             return mInvalidSizeStr.toString();
    265         }
    266         return Formatter.formatFileSize(getActivity(), size);
    267     }
    268 
    269     private void refreshSizeInfo() {
    270         if (mAppEntry.size == ApplicationsState.SIZE_INVALID
    271                 || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
    272             mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
    273             if (!mHaveSizes) {
    274                 mAppSize.setSummary(mComputingStr);
    275                 mDataSize.setSummary(mComputingStr);
    276                 mCacheSize.setSummary(mComputingStr);
    277                 mTotalSize.setSummary(mComputingStr);
    278             }
    279             mClearDataButton.setEnabled(false);
    280             mClearCacheButton.setEnabled(false);
    281         } else {
    282             mHaveSizes = true;
    283             long codeSize = mAppEntry.codeSize;
    284             long dataSize = mAppEntry.dataSize;
    285             if (Environment.isExternalStorageEmulated()) {
    286                 codeSize += mAppEntry.externalCodeSize;
    287                 dataSize +=  mAppEntry.externalDataSize;
    288             } else {
    289                 if (mLastExternalCodeSize != mAppEntry.externalCodeSize) {
    290                     mLastExternalCodeSize = mAppEntry.externalCodeSize;
    291                     mExternalCodeSize.setSummary(getSizeStr(mAppEntry.externalCodeSize));
    292                 }
    293                 if (mLastExternalDataSize !=  mAppEntry.externalDataSize) {
    294                     mLastExternalDataSize =  mAppEntry.externalDataSize;
    295                     mExternalDataSize.setSummary(getSizeStr( mAppEntry.externalDataSize));
    296                 }
    297             }
    298             if (mLastCodeSize != codeSize) {
    299                 mLastCodeSize = codeSize;
    300                 mAppSize.setSummary(getSizeStr(codeSize));
    301             }
    302             if (mLastDataSize != dataSize) {
    303                 mLastDataSize = dataSize;
    304                 mDataSize.setSummary(getSizeStr(dataSize));
    305             }
    306             long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize;
    307             if (mLastCacheSize != cacheSize) {
    308                 mLastCacheSize = cacheSize;
    309                 mCacheSize.setSummary(getSizeStr(cacheSize));
    310             }
    311             if (mLastTotalSize != mAppEntry.size) {
    312                 mLastTotalSize = mAppEntry.size;
    313                 mTotalSize.setSummary(getSizeStr(mAppEntry.size));
    314             }
    315 
    316             if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) {
    317                 mClearDataButton.setEnabled(false);
    318             } else {
    319                 mClearDataButton.setEnabled(true);
    320                 mClearDataButton.setOnClickListener(this);
    321             }
    322             if (cacheSize <= 0) {
    323                 mClearCacheButton.setEnabled(false);
    324             } else {
    325                 mClearCacheButton.setEnabled(true);
    326                 mClearCacheButton.setOnClickListener(this);
    327             }
    328         }
    329         if (mAppsControlDisallowedBySystem) {
    330             mClearCacheButton.setEnabled(false);
    331             mClearDataButton.setEnabled(false);
    332         }
    333     }
    334 
    335     @Override
    336     protected boolean refreshUi() {
    337         retrieveAppEntry();
    338         if (mAppEntry == null) {
    339             return false;
    340         }
    341         refreshSizeInfo();
    342         refreshGrantedUriPermissions();
    343 
    344         final VolumeInfo currentVol = getActivity().getPackageManager()
    345                 .getPackageCurrentVolume(mAppEntry.info);
    346         final StorageManager storage = getContext().getSystemService(StorageManager.class);
    347         mStorageUsed.setSummary(storage.getBestVolumeDescription(currentVol));
    348 
    349         refreshButtons();
    350 
    351         return true;
    352     }
    353 
    354     private void refreshButtons() {
    355         initMoveDialog();
    356         initDataButtons();
    357     }
    358 
    359     private void initDataButtons() {
    360         final boolean appHasSpaceManagementUI = mAppEntry.info.manageSpaceActivityName != null;
    361         final boolean appHasActiveAdmins = mDpm.packageHasActiveAdmins(mPackageName);
    362         // Check that SYSTEM_APP flag is set, and ALLOW_CLEAR_USER_DATA is not set.
    363         final boolean isNonClearableSystemApp =
    364                 (mAppEntry.info.flags & (FLAG_SYSTEM | FLAG_ALLOW_CLEAR_USER_DATA)) == FLAG_SYSTEM;
    365         final boolean appRestrictsClearingData = isNonClearableSystemApp || appHasActiveAdmins;
    366 
    367         final Intent intent = new Intent(Intent.ACTION_DEFAULT);
    368         if (appHasSpaceManagementUI) {
    369             intent.setClassName(mAppEntry.info.packageName, mAppEntry.info.manageSpaceActivityName);
    370         }
    371         final boolean isManageSpaceActivityAvailable =
    372                 getPackageManager().resolveActivity(intent, 0) != null;
    373 
    374         if ((!appHasSpaceManagementUI && appRestrictsClearingData)
    375                 || !isManageSpaceActivityAvailable) {
    376             mClearDataButton.setText(R.string.clear_user_data_text);
    377             mClearDataButton.setEnabled(false);
    378             mCanClearData = false;
    379         } else {
    380             if (appHasSpaceManagementUI) {
    381                 mClearDataButton.setText(R.string.manage_space_text);
    382             } else {
    383                 mClearDataButton.setText(R.string.clear_user_data_text);
    384             }
    385             mClearDataButton.setOnClickListener(this);
    386         }
    387 
    388         if (mAppsControlDisallowedBySystem) {
    389             mClearDataButton.setEnabled(false);
    390         }
    391     }
    392 
    393     private void initMoveDialog() {
    394         final Context context = getActivity();
    395         final StorageManager storage = context.getSystemService(StorageManager.class);
    396 
    397         final List<VolumeInfo> candidates = context.getPackageManager()
    398                 .getPackageCandidateVolumes(mAppEntry.info);
    399         if (candidates.size() > 1) {
    400             Collections.sort(candidates, VolumeInfo.getDescriptionComparator());
    401 
    402             CharSequence[] labels = new CharSequence[candidates.size()];
    403             int current = -1;
    404             for (int i = 0; i < candidates.size(); i++) {
    405                 final String volDescrip = storage.getBestVolumeDescription(candidates.get(i));
    406                 if (Objects.equals(volDescrip, mStorageUsed.getSummary())) {
    407                     current = i;
    408                 }
    409                 labels[i] = volDescrip;
    410             }
    411             mCandidates = candidates.toArray(new VolumeInfo[candidates.size()]);
    412             mDialogBuilder = new AlertDialog.Builder(getContext())
    413                     .setTitle(R.string.change_storage)
    414                     .setSingleChoiceItems(labels, current, this)
    415                     .setNegativeButton(R.string.cancel, null);
    416         } else {
    417             removePreference(KEY_STORAGE_USED);
    418             removePreference(KEY_CHANGE_STORAGE);
    419             removePreference(KEY_STORAGE_SPACE);
    420         }
    421     }
    422 
    423     /*
    424      * Private method to initiate clearing user data when the user clicks the clear data
    425      * button for a system package
    426      */
    427     private void initiateClearUserData() {
    428         mClearDataButton.setEnabled(false);
    429         // Invoke uninstall or clear user data based on sysPackage
    430         String packageName = mAppEntry.info.packageName;
    431         Log.i(TAG, "Clearing user data for package : " + packageName);
    432         if (mClearDataObserver == null) {
    433             mClearDataObserver = new ClearUserDataObserver();
    434         }
    435         ActivityManager am = (ActivityManager)
    436                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
    437         boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
    438         if (!res) {
    439             // Clearing data failed for some obscure reason. Just log error for now
    440             Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
    441             showDialogInner(DLG_CANNOT_CLEAR_DATA, 0);
    442         } else {
    443             mClearDataButton.setText(R.string.recompute_size);
    444         }
    445     }
    446 
    447     /*
    448      * Private method to handle clear message notification from observer when
    449      * the async operation from PackageManager is complete
    450      */
    451     private void processClearMsg(Message msg) {
    452         int result = msg.arg1;
    453         String packageName = mAppEntry.info.packageName;
    454         mClearDataButton.setText(R.string.clear_user_data_text);
    455         if (result == OP_SUCCESSFUL) {
    456             Log.i(TAG, "Cleared user data for package : "+packageName);
    457             mState.requestSize(mPackageName, mUserId);
    458         } else {
    459             mClearDataButton.setEnabled(true);
    460         }
    461     }
    462 
    463     private void refreshGrantedUriPermissions() {
    464         // Clear UI first (in case the activity has been resumed)
    465         removeUriPermissionsFromUi();
    466 
    467         // Gets all URI permissions from am.
    468         ActivityManager am = (ActivityManager) getActivity().getSystemService(
    469                 Context.ACTIVITY_SERVICE);
    470         List<UriPermission> perms =
    471                 am.getGrantedUriPermissions(mAppEntry.info.packageName).getList();
    472 
    473         if (perms.isEmpty()) {
    474             mClearUriButton.setVisibility(View.GONE);
    475             return;
    476         }
    477 
    478         PackageManager pm = getActivity().getPackageManager();
    479 
    480         // Group number of URIs by app.
    481         Map<CharSequence, MutableInt> uriCounters = new TreeMap<>();
    482         for (UriPermission perm : perms) {
    483             String authority = perm.getUri().getAuthority();
    484             ProviderInfo provider = pm.resolveContentProvider(authority, 0);
    485             CharSequence app = provider.applicationInfo.loadLabel(pm);
    486             MutableInt count = uriCounters.get(app);
    487             if (count == null) {
    488                 uriCounters.put(app, new MutableInt(1));
    489             } else {
    490                 count.value++;
    491             }
    492         }
    493 
    494         // Dynamically add the preferences, one per app.
    495         int order = 0;
    496         for (Map.Entry<CharSequence, MutableInt> entry : uriCounters.entrySet()) {
    497             int numberResources = entry.getValue().value;
    498             Preference pref = new Preference(getPrefContext());
    499             pref.setTitle(entry.getKey());
    500             pref.setSummary(getPrefContext().getResources()
    501                     .getQuantityString(R.plurals.uri_permissions_text, numberResources,
    502                             numberResources));
    503             pref.setSelectable(false);
    504             pref.setLayoutResource(R.layout.horizontal_preference);
    505             pref.setOrder(order);
    506             Log.v(TAG, "Adding preference '" + pref + "' at order " + order);
    507             mUri.addPreference(pref);
    508         }
    509 
    510         if (mAppsControlDisallowedBySystem) {
    511             mClearUriButton.setEnabled(false);
    512         }
    513 
    514         mClearUri.setOrder(order);
    515         mClearUriButton.setVisibility(View.VISIBLE);
    516 
    517     }
    518 
    519     private void clearUriPermissions() {
    520         // Synchronously revoke the permissions.
    521         final ActivityManager am = (ActivityManager) getActivity().getSystemService(
    522                 Context.ACTIVITY_SERVICE);
    523         am.clearGrantedUriPermissions(mAppEntry.info.packageName);
    524 
    525         // Update UI
    526         refreshGrantedUriPermissions();
    527     }
    528 
    529     private void removeUriPermissionsFromUi() {
    530         // Remove all preferences but the clear button.
    531         int count = mUri.getPreferenceCount();
    532         for (int i = count - 1; i >= 0; i--) {
    533             Preference pref = mUri.getPreference(i);
    534             if (pref != mClearUri) {
    535                 mUri.removePreference(pref);
    536             }
    537         }
    538     }
    539 
    540     @Override
    541     protected AlertDialog createDialog(int id, int errorCode) {
    542         switch (id) {
    543             case DLG_CLEAR_DATA:
    544                 return new AlertDialog.Builder(getActivity())
    545                         .setTitle(getActivity().getText(R.string.clear_data_dlg_title))
    546                         .setMessage(getActivity().getText(R.string.clear_data_dlg_text))
    547                         .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
    548                             public void onClick(DialogInterface dialog, int which) {
    549                                 // Clear user data here
    550                                 initiateClearUserData();
    551                             }
    552                         })
    553                         .setNegativeButton(R.string.dlg_cancel, null)
    554                         .create();
    555             case DLG_CANNOT_CLEAR_DATA:
    556                 return new AlertDialog.Builder(getActivity())
    557                         .setTitle(getActivity().getText(R.string.clear_failed_dlg_title))
    558                         .setMessage(getActivity().getText(R.string.clear_failed_dlg_text))
    559                         .setNeutralButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
    560                             public void onClick(DialogInterface dialog, int which) {
    561                                 mClearDataButton.setEnabled(false);
    562                                 //force to recompute changed value
    563                                 setIntentAndFinish(false, false);
    564                             }
    565                         })
    566                         .create();
    567         }
    568         return null;
    569     }
    570 
    571     @Override
    572     public void onPackageSizeChanged(String packageName) {
    573         if (packageName.equals(mAppEntry.info.packageName)) {
    574             refreshSizeInfo();
    575         }
    576     }
    577 
    578     private final Handler mHandler = new Handler() {
    579         public void handleMessage(Message msg) {
    580             if (getView() == null) {
    581                 return;
    582             }
    583             switch (msg.what) {
    584                 case MSG_CLEAR_USER_DATA:
    585                     processClearMsg(msg);
    586                     break;
    587                 case MSG_CLEAR_CACHE:
    588                     // Refresh size info
    589                     mState.requestSize(mPackageName, mUserId);
    590                     break;
    591             }
    592         }
    593     };
    594 
    595     public static CharSequence getSummary(AppEntry appEntry, Context context) {
    596         if (appEntry.size == ApplicationsState.SIZE_INVALID
    597                 || appEntry.size == ApplicationsState.SIZE_UNKNOWN) {
    598             return context.getText(R.string.computing_size);
    599         } else {
    600             CharSequence storageType = context.getString(
    601                     (appEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0
    602                     ? R.string.storage_type_external
    603                     : R.string.storage_type_internal);
    604             return context.getString(R.string.storage_summary_format,
    605                     getSize(appEntry, context), storageType);
    606         }
    607     }
    608 
    609     private static CharSequence getSize(AppEntry appEntry, Context context) {
    610         long size = appEntry.size;
    611         if (size == SIZE_INVALID) {
    612             return context.getText(R.string.invalid_size_value);
    613         }
    614         return Formatter.formatFileSize(context, size);
    615     }
    616 
    617     @Override
    618     protected int getMetricsCategory() {
    619         return MetricsEvent.APPLICATIONS_APP_STORAGE;
    620     }
    621 
    622     class ClearCacheObserver extends IPackageDataObserver.Stub {
    623         public void onRemoveCompleted(final String packageName, final boolean succeeded) {
    624             final Message msg = mHandler.obtainMessage(MSG_CLEAR_CACHE);
    625             msg.arg1 = succeeded ? OP_SUCCESSFUL : OP_FAILED;
    626             mHandler.sendMessage(msg);
    627         }
    628     }
    629 
    630     class ClearUserDataObserver extends IPackageDataObserver.Stub {
    631        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
    632            final Message msg = mHandler.obtainMessage(MSG_CLEAR_USER_DATA);
    633            msg.arg1 = succeeded ? OP_SUCCESSFUL : OP_FAILED;
    634            mHandler.sendMessage(msg);
    635         }
    636     }
    637 }
    638