InstalledAppDetails.java revision 2715376cfa4d220f183623a5f35214d1bd537376
1/**
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16
17package com.android.settings.applications;
18
19import com.android.settings.R;
20import com.android.settings.applications.ApplicationsState.AppEntry;
21
22import android.app.Activity;
23import android.app.ActivityManager;
24import android.app.AlertDialog;
25import android.app.Dialog;
26import android.app.DialogFragment;
27import android.app.Fragment;
28import android.content.BroadcastReceiver;
29import android.content.Context;
30import android.content.DialogInterface;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageDataObserver;
35import android.content.pm.IPackageMoveObserver;
36import android.content.pm.PackageInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.ResolveInfo;
39import android.content.pm.PackageManager.NameNotFoundException;
40import android.hardware.usb.IUsbManager;
41import android.net.Uri;
42import android.os.AsyncTask;
43import android.os.Bundle;
44import android.os.Environment;
45import android.os.Handler;
46import android.os.IBinder;
47import android.os.Message;
48import android.os.RemoteException;
49import android.os.ServiceManager;
50import android.preference.PreferenceActivity;
51import android.text.format.Formatter;
52import android.util.Log;
53
54import java.lang.ref.WeakReference;
55import java.util.ArrayList;
56import java.util.List;
57import android.content.ComponentName;
58import android.view.LayoutInflater;
59import android.view.View;
60import android.view.ViewGroup;
61import android.widget.AppSecurityPermissions;
62import android.widget.Button;
63import android.widget.ImageView;
64import android.widget.LinearLayout;
65import android.widget.TextView;
66
67/**
68 * Activity to display application information from Settings. This activity presents
69 * extended information associated with a package like code, data, total size, permissions
70 * used by the application and also the set of default launchable activities.
71 * For system applications, an option to clear user data is displayed only if data size is > 0.
72 * System applications that do not want clear user data do not have this option.
73 * For non-system applications, there is no option to clear data. Instead there is an option to
74 * uninstall the application.
75 */
76public class InstalledAppDetails extends Fragment
77        implements View.OnClickListener, ApplicationsState.Callbacks {
78    private static final String TAG="InstalledAppDetails";
79    static final boolean SUPPORT_DISABLE_APPS = false;
80    private static final boolean localLOGV = false;
81
82    public static final String ARG_PACKAGE_NAME = "package";
83
84    private PackageManager mPm;
85    private IUsbManager mUsbManager;
86    private ApplicationsState mState;
87    private ApplicationsState.AppEntry mAppEntry;
88    private PackageInfo mPackageInfo;
89    private CanBeOnSdCardChecker mCanBeOnSdCardChecker;
90    private View mRootView;
91    private Button mUninstallButton;
92    private boolean mMoveInProgress = false;
93    private boolean mUpdatedSysApp = false;
94    private Button mActivitiesButton;
95    private boolean mCanClearData = true;
96    private TextView mAppVersion;
97    private TextView mTotalSize;
98    private TextView mAppSize;
99    private TextView mDataSize;
100    private TextView mExternalSize;
101    private ClearUserDataObserver mClearDataObserver;
102    // Views related to cache info
103    private TextView mCacheSize;
104    private Button mClearCacheButton;
105    private ClearCacheObserver mClearCacheObserver;
106    private Button mForceStopButton;
107    private Button mClearDataButton;
108    private Button mMoveAppButton;
109
110    private PackageMoveObserver mPackageMoveObserver;
111
112    private boolean mHaveSizes = false;
113    private long mLastCodeSize = -1;
114    private long mLastDataSize = -1;
115    private long mLastExternalSize = -1;
116    private long mLastCacheSize = -1;
117    private long mLastTotalSize = -1;
118
119    //internal constants used in Handler
120    private static final int OP_SUCCESSFUL = 1;
121    private static final int OP_FAILED = 2;
122    private static final int CLEAR_USER_DATA = 1;
123    private static final int CLEAR_CACHE = 3;
124    private static final int PACKAGE_MOVE = 4;
125
126    // invalid size value used initially and also when size retrieval through PackageManager
127    // fails for whatever reason
128    private static final int SIZE_INVALID = -1;
129
130    // Resource strings
131    private CharSequence mInvalidSizeStr;
132    private CharSequence mComputingStr;
133
134    // Dialog identifiers used in showDialog
135    private static final int DLG_BASE = 0;
136    private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
137    private static final int DLG_FACTORY_RESET = DLG_BASE + 2;
138    private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3;
139    private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4;
140    private static final int DLG_FORCE_STOP = DLG_BASE + 5;
141    private static final int DLG_MOVE_FAILED = DLG_BASE + 6;
142
143    private Handler mHandler = new Handler() {
144        public void handleMessage(Message msg) {
145            // If the fragment is gone, don't process any more messages.
146            if (getView() == null) {
147                return;
148            }
149            switch (msg.what) {
150                case CLEAR_USER_DATA:
151                    processClearMsg(msg);
152                    break;
153                case CLEAR_CACHE:
154                    // Refresh size info
155                    mState.requestSize(mAppEntry.info.packageName);
156                    break;
157                case PACKAGE_MOVE:
158                    processMoveMsg(msg);
159                    break;
160                default:
161                    break;
162            }
163        }
164    };
165
166    class ClearUserDataObserver extends IPackageDataObserver.Stub {
167       public void onRemoveCompleted(final String packageName, final boolean succeeded) {
168           final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
169           msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
170           mHandler.sendMessage(msg);
171        }
172    }
173
174    class ClearCacheObserver extends IPackageDataObserver.Stub {
175        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
176            final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
177            msg.arg1 = succeeded ? OP_SUCCESSFUL:OP_FAILED;
178            mHandler.sendMessage(msg);
179         }
180     }
181
182    class PackageMoveObserver extends IPackageMoveObserver.Stub {
183        public void packageMoved(String packageName, int returnCode) throws RemoteException {
184            final Message msg = mHandler.obtainMessage(PACKAGE_MOVE);
185            msg.arg1 = returnCode;
186            mHandler.sendMessage(msg);
187        }
188    }
189
190    private String getSizeStr(long size) {
191        if (size == SIZE_INVALID) {
192            return mInvalidSizeStr.toString();
193        }
194        return Formatter.formatFileSize(getActivity(), size);
195    }
196
197    private void initDataButtons() {
198        if ((mAppEntry.info.flags&(ApplicationInfo.FLAG_SYSTEM
199                | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA))
200                == ApplicationInfo.FLAG_SYSTEM) {
201            mClearDataButton.setText(R.string.clear_user_data_text);
202            mClearDataButton.setEnabled(false);
203            mCanClearData = false;
204        } else {
205            if (mAppEntry.info.manageSpaceActivityName != null) {
206                mClearDataButton.setText(R.string.manage_space_text);
207            } else {
208                mClearDataButton.setText(R.string.clear_user_data_text);
209            }
210            mClearDataButton.setOnClickListener(this);
211        }
212    }
213
214    private CharSequence getMoveErrMsg(int errCode) {
215        switch (errCode) {
216            case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE:
217                return getActivity().getString(R.string.insufficient_storage);
218            case PackageManager.MOVE_FAILED_DOESNT_EXIST:
219                return getActivity().getString(R.string.does_not_exist);
220            case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
221                return getActivity().getString(R.string.app_forward_locked);
222            case PackageManager.MOVE_FAILED_INVALID_LOCATION:
223                return getActivity().getString(R.string.invalid_location);
224            case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
225                return getActivity().getString(R.string.system_package);
226            case PackageManager.MOVE_FAILED_INTERNAL_ERROR:
227                return "";
228        }
229        return "";
230    }
231
232    private void initMoveButton() {
233        if (Environment.isExternalStorageEmulated()) {
234            mMoveAppButton.setVisibility(View.INVISIBLE);
235            return;
236        }
237        boolean dataOnly = false;
238        dataOnly = (mPackageInfo == null) && (mAppEntry != null);
239        boolean moveDisable = true;
240        if (dataOnly) {
241            mMoveAppButton.setText(R.string.move_app);
242        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
243            mMoveAppButton.setText(R.string.move_app_to_internal);
244            // Always let apps move to internal storage from sdcard.
245            moveDisable = false;
246        } else {
247            mMoveAppButton.setText(R.string.move_app_to_sdcard);
248            mCanBeOnSdCardChecker.init();
249            moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info);
250        }
251        if (moveDisable) {
252            mMoveAppButton.setEnabled(false);
253        } else {
254            mMoveAppButton.setOnClickListener(this);
255            mMoveAppButton.setEnabled(true);
256        }
257    }
258
259    private void initUninstallButtons() {
260        mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
261        boolean enabled = true;
262        if (mUpdatedSysApp) {
263            mUninstallButton.setText(R.string.app_factory_reset);
264        } else {
265            if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
266                enabled = false;
267                if (SUPPORT_DISABLE_APPS) {
268                    try {
269                        // Try to prevent the user from bricking their phone
270                        // by not allowing disabling of apps signed with the
271                        // system cert and any launcher app in the system.
272                        PackageInfo sys = mPm.getPackageInfo("android",
273                                PackageManager.GET_SIGNATURES);
274                        Intent intent = new Intent(Intent.ACTION_MAIN);
275                        intent.addCategory(Intent.CATEGORY_HOME);
276                        intent.setPackage(mAppEntry.info.packageName);
277                        List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
278                        if ((homes != null && homes.size() > 0) ||
279                                (mPackageInfo != null &&
280                                        sys.signatures[0].equals(mPackageInfo.signatures[0]))) {
281                            // Disable button for core system applications.
282                            mUninstallButton.setText(R.string.disable_text);
283                        } else if (mAppEntry.info.enabled) {
284                            mUninstallButton.setText(R.string.disable_text);
285                            enabled = true;
286                        } else {
287                            mUninstallButton.setText(R.string.enable_text);
288                            enabled = true;
289                        }
290                    } catch (PackageManager.NameNotFoundException e) {
291                        Log.w(TAG, "Unable to get package info", e);
292                    }
293                }
294            } else {
295                mUninstallButton.setText(R.string.uninstall_text);
296            }
297        }
298        mUninstallButton.setEnabled(enabled);
299        if (enabled) {
300            // Register listener
301            mUninstallButton.setOnClickListener(this);
302        }
303    }
304
305    /** Called when the activity is first created. */
306    @Override
307    public void onCreate(Bundle icicle) {
308        super.onCreate(icicle);
309
310        mState = ApplicationsState.getInstance(getActivity().getApplication());
311        mPm = getActivity().getPackageManager();
312        IBinder b = ServiceManager.getService(Context.USB_SERVICE);
313        mUsbManager = IUsbManager.Stub.asInterface(b);
314
315        mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
316    }
317
318    @Override
319    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
320        View view = mRootView = inflater.inflate(R.layout.installed_app_details, null);
321
322        mComputingStr = getActivity().getText(R.string.computing_size);
323
324        // Set default values on sizes
325        mTotalSize = (TextView)view.findViewById(R.id.total_size_text);
326        mAppSize = (TextView)view.findViewById(R.id.application_size_text);
327        mDataSize = (TextView)view.findViewById(R.id.data_size_text);
328        mExternalSize = (TextView)view.findViewById(R.id.external_size_text);
329
330        // Get Control button panel
331        View btnPanel = view.findViewById(R.id.control_buttons_panel);
332        mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button);
333        mForceStopButton.setText(R.string.force_stop);
334        mUninstallButton = (Button)btnPanel.findViewById(R.id.right_button);
335        mForceStopButton.setEnabled(false);
336
337        // Initialize clear data and move install location buttons
338        View data_buttons_panel = view.findViewById(R.id.data_buttons_panel);
339        mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.left_button);
340        mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.right_button);
341
342        // Cache section
343        mCacheSize = (TextView) view.findViewById(R.id.cache_size_text);
344        mClearCacheButton = (Button) view.findViewById(R.id.clear_cache_button);
345
346        mActivitiesButton = (Button)view.findViewById(R.id.clear_activities_button);
347
348        return view;
349    }
350
351    // Utility method to set applicaiton label and icon.
352    private void setAppLabelAndIcon(PackageInfo pkgInfo) {
353        View appSnippet = mRootView.findViewById(R.id.app_snippet);
354        ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon);
355        mState.ensureIcon(mAppEntry);
356        icon.setImageDrawable(mAppEntry.icon);
357        // Set application name.
358        TextView label = (TextView) appSnippet.findViewById(R.id.app_name);
359        label.setText(mAppEntry.label);
360        // Version number of application
361        mAppVersion = (TextView) appSnippet.findViewById(R.id.app_size);
362
363        if (pkgInfo != null && pkgInfo.versionName != null) {
364            mAppVersion.setVisibility(View.VISIBLE);
365            mAppVersion.setText(getActivity().getString(R.string.version_text,
366                    String.valueOf(pkgInfo.versionName)));
367        } else {
368            mAppVersion.setVisibility(View.INVISIBLE);
369        }
370    }
371
372    @Override
373    public void onResume() {
374        super.onResume();
375
376        mState.resume(this);
377        if (!refreshUi()) {
378            setIntentAndFinish(true, true);
379        }
380    }
381
382    @Override
383    public void onPause() {
384        super.onPause();
385        mState.pause();
386    }
387
388    @Override
389    public void onAllSizesComputed() {
390    }
391
392    @Override
393    public void onPackageIconChanged() {
394    }
395
396    @Override
397    public void onPackageListChanged() {
398        refreshUi();
399    }
400
401    @Override
402    public void onRebuildComplete(ArrayList<AppEntry> apps) {
403    }
404
405    @Override
406    public void onPackageSizeChanged(String packageName) {
407        if (packageName.equals(mAppEntry.info.packageName)) {
408            refreshSizeInfo();
409        }
410    }
411
412    @Override
413    public void onRunningStateChanged(boolean running) {
414    }
415
416    private boolean refreshUi() {
417        if (mMoveInProgress) {
418            return true;
419        }
420        final Bundle args = getArguments();
421        String packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
422        if (packageName == null) {
423            Intent intent = (args == null) ?
424                    getActivity().getIntent() : (Intent) args.getParcelable("intent");
425            if (intent != null) {
426                packageName = intent.getData().getSchemeSpecificPart();
427            }
428        }
429        mAppEntry = mState.getEntry(packageName);
430
431        if (mAppEntry == null) {
432            return false; // onCreate must have failed, make sure to exit
433        }
434
435        // Get application info again to refresh changed properties of application
436        try {
437            mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
438                    PackageManager.GET_DISABLED_COMPONENTS |
439                    PackageManager.GET_UNINSTALLED_PACKAGES |
440                    PackageManager.GET_SIGNATURES);
441        } catch (NameNotFoundException e) {
442            Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
443            return false; // onCreate must have failed, make sure to exit
444        }
445
446        // Get list of preferred activities
447        List<ComponentName> prefActList = new ArrayList<ComponentName>();
448
449        // Intent list cannot be null. so pass empty list
450        List<IntentFilter> intentList = new ArrayList<IntentFilter>();
451        mPm.getPreferredActivities(intentList, prefActList, packageName);
452        if(localLOGV) Log.i(TAG, "Have "+prefActList.size()+" number of activities in prefered list");
453        boolean hasUsbDefaults = false;
454        try {
455            hasUsbDefaults = mUsbManager.hasDefaults(packageName);
456        } catch (RemoteException e) {
457            Log.e(TAG, "mUsbManager.hasDefaults", e);
458        }
459        TextView autoLaunchView = (TextView)mRootView.findViewById(R.id.auto_launch);
460        if (prefActList.size() <= 0 && !hasUsbDefaults) {
461            // Disable clear activities button
462            autoLaunchView.setText(R.string.auto_launch_disable_text);
463            mActivitiesButton.setEnabled(false);
464        } else {
465            autoLaunchView.setText(R.string.auto_launch_enable_text);
466            mActivitiesButton.setEnabled(true);
467            mActivitiesButton.setOnClickListener(this);
468        }
469
470        // Security permissions section
471        LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section);
472        AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName);
473        if (asp.getPermissionCount() > 0) {
474            permsView.setVisibility(View.VISIBLE);
475            // Make the security sections header visible
476            LinearLayout securityList = (LinearLayout) permsView.findViewById(
477                    R.id.security_settings_list);
478            securityList.removeAllViews();
479            securityList.addView(asp.getPermissionsView());
480        } else {
481            permsView.setVisibility(View.GONE);
482        }
483
484        checkForceStop();
485        setAppLabelAndIcon(mPackageInfo);
486        refreshButtons();
487        refreshSizeInfo();
488        return true;
489    }
490
491    private void setIntentAndFinish(boolean finish, boolean appChanged) {
492        if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
493        Intent intent = new Intent();
494        intent.putExtra(ManageApplications.APP_CHG, appChanged);
495        PreferenceActivity pa = (PreferenceActivity)getActivity();
496        pa.finishPreferencePanel(this, Activity.RESULT_OK, intent);
497    }
498
499    private void refreshSizeInfo() {
500        if (mAppEntry.size == ApplicationsState.SIZE_INVALID
501                || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
502            mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
503            if (!mHaveSizes) {
504                mAppSize.setText(mComputingStr);
505                mDataSize.setText(mComputingStr);
506                mCacheSize.setText(mComputingStr);
507                mTotalSize.setText(mComputingStr);
508            }
509            mClearDataButton.setEnabled(false);
510            mClearCacheButton.setEnabled(false);
511
512        } else {
513            mHaveSizes = true;
514            if (mLastCodeSize != mAppEntry.codeSize) {
515                mLastCodeSize = mAppEntry.codeSize;
516                mAppSize.setText(getSizeStr(mAppEntry.codeSize));
517            }
518            if (mLastDataSize != mAppEntry.dataSize) {
519                mLastDataSize = mAppEntry.dataSize;
520                mDataSize.setText(getSizeStr(mAppEntry.dataSize));
521            }
522            if (mLastExternalSize != mAppEntry.externalSize) {
523                mLastExternalSize = mAppEntry.externalSize;
524                mExternalSize.setText(getSizeStr(mAppEntry.externalSize));
525            }
526            if (mLastCacheSize != mAppEntry.cacheSize) {
527                mLastCacheSize = mAppEntry.cacheSize;
528                mCacheSize.setText(getSizeStr(mAppEntry.cacheSize));
529            }
530            if (mLastTotalSize != mAppEntry.size) {
531                mLastTotalSize = mAppEntry.size;
532                mTotalSize.setText(getSizeStr(mAppEntry.size));
533            }
534
535            if (mAppEntry.dataSize <= 0 || !mCanClearData) {
536                mClearDataButton.setEnabled(false);
537            } else {
538                mClearDataButton.setEnabled(true);
539                mClearDataButton.setOnClickListener(this);
540            }
541            if (mAppEntry.cacheSize <= 0) {
542                mClearCacheButton.setEnabled(false);
543            } else {
544                mClearCacheButton.setEnabled(true);
545                mClearCacheButton.setOnClickListener(this);
546            }
547        }
548    }
549
550    /*
551     * Private method to handle clear message notification from observer when
552     * the async operation from PackageManager is complete
553     */
554    private void processClearMsg(Message msg) {
555        int result = msg.arg1;
556        String packageName = mAppEntry.info.packageName;
557        mClearDataButton.setText(R.string.clear_user_data_text);
558        if(result == OP_SUCCESSFUL) {
559            Log.i(TAG, "Cleared user data for package : "+packageName);
560            mState.requestSize(mAppEntry.info.packageName);
561        } else {
562            mClearDataButton.setEnabled(true);
563        }
564        checkForceStop();
565    }
566
567    private void refreshButtons() {
568        if (!mMoveInProgress) {
569            initUninstallButtons();
570            initDataButtons();
571            initMoveButton();
572        } else {
573            mMoveAppButton.setText(R.string.moving);
574            mMoveAppButton.setEnabled(false);
575            mUninstallButton.setEnabled(false);
576        }
577    }
578
579    private void processMoveMsg(Message msg) {
580        int result = msg.arg1;
581        String packageName = mAppEntry.info.packageName;
582        // Refresh the button attributes.
583        mMoveInProgress = false;
584        if (result == PackageManager.MOVE_SUCCEEDED) {
585            Log.i(TAG, "Moved resources for " + packageName);
586            // Refresh size information again.
587            mState.requestSize(mAppEntry.info.packageName);
588        } else {
589            showDialogInner(DLG_MOVE_FAILED, result);
590        }
591        refreshUi();
592    }
593
594    /*
595     * Private method to initiate clearing user data when the user clicks the clear data
596     * button for a system package
597     */
598    private  void initiateClearUserData() {
599        mClearDataButton.setEnabled(false);
600        // Invoke uninstall or clear user data based on sysPackage
601        String packageName = mAppEntry.info.packageName;
602        Log.i(TAG, "Clearing user data for package : " + packageName);
603        if (mClearDataObserver == null) {
604            mClearDataObserver = new ClearUserDataObserver();
605        }
606        ActivityManager am = (ActivityManager)
607                getActivity().getSystemService(Context.ACTIVITY_SERVICE);
608        boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
609        if (!res) {
610            // Clearing data failed for some obscure reason. Just log error for now
611            Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
612            showDialogInner(DLG_CANNOT_CLEAR_DATA, 0);
613        } else {
614            mClearDataButton.setText(R.string.recompute_size);
615        }
616    }
617
618    private void showDialogInner(int id, int moveErrorCode) {
619        DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode);
620        newFragment.setTargetFragment(this, 0);
621        newFragment.show(getFragmentManager(), "dialog " + id);
622    }
623
624    public static class MyAlertDialogFragment extends DialogFragment {
625
626        public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) {
627            MyAlertDialogFragment frag = new MyAlertDialogFragment();
628            Bundle args = new Bundle();
629            args.putInt("id", id);
630            args.putInt("moveError", moveErrorCode);
631            frag.setArguments(args);
632            return frag;
633        }
634
635        InstalledAppDetails getOwner() {
636            return (InstalledAppDetails)getTargetFragment();
637        }
638
639        @Override
640        public Dialog onCreateDialog(Bundle savedInstanceState) {
641            int id = getArguments().getInt("id");
642            int moveErrorCode = getArguments().getInt("moveError");
643            switch (id) {
644                case DLG_CLEAR_DATA:
645                    return new AlertDialog.Builder(getActivity())
646                    .setTitle(getActivity().getText(R.string.clear_data_dlg_title))
647                    .setIcon(android.R.drawable.ic_dialog_alert)
648                    .setMessage(getActivity().getText(R.string.clear_data_dlg_text))
649                    .setPositiveButton(R.string.dlg_ok,
650                            new DialogInterface.OnClickListener() {
651                        public void onClick(DialogInterface dialog, int which) {
652                            // Clear user data here
653                            getOwner().initiateClearUserData();
654                        }
655                    })
656                    .setNegativeButton(R.string.dlg_cancel, null)
657                    .create();
658                case DLG_FACTORY_RESET:
659                    return new AlertDialog.Builder(getActivity())
660                    .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title))
661                    .setIcon(android.R.drawable.ic_dialog_alert)
662                    .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text))
663                    .setPositiveButton(R.string.dlg_ok,
664                            new DialogInterface.OnClickListener() {
665                        public void onClick(DialogInterface dialog, int which) {
666                            // Clear user data here
667                            getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName);
668                        }
669                    })
670                    .setNegativeButton(R.string.dlg_cancel, null)
671                    .create();
672                case DLG_APP_NOT_FOUND:
673                    return new AlertDialog.Builder(getActivity())
674                    .setTitle(getActivity().getText(R.string.app_not_found_dlg_title))
675                    .setIcon(android.R.drawable.ic_dialog_alert)
676                    .setMessage(getActivity().getText(R.string.app_not_found_dlg_title))
677                    .setNeutralButton(getActivity().getText(R.string.dlg_ok),
678                            new DialogInterface.OnClickListener() {
679                        public void onClick(DialogInterface dialog, int which) {
680                            //force to recompute changed value
681                            getOwner().setIntentAndFinish(true, true);
682                        }
683                    })
684                    .create();
685                case DLG_CANNOT_CLEAR_DATA:
686                    return new AlertDialog.Builder(getActivity())
687                    .setTitle(getActivity().getText(R.string.clear_failed_dlg_title))
688                    .setIcon(android.R.drawable.ic_dialog_alert)
689                    .setMessage(getActivity().getText(R.string.clear_failed_dlg_text))
690                    .setNeutralButton(R.string.dlg_ok,
691                            new DialogInterface.OnClickListener() {
692                        public void onClick(DialogInterface dialog, int which) {
693                            getOwner().mClearDataButton.setEnabled(false);
694                            //force to recompute changed value
695                            getOwner().setIntentAndFinish(false, false);
696                        }
697                    })
698                    .create();
699                case DLG_FORCE_STOP:
700                    return new AlertDialog.Builder(getActivity())
701                    .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
702                    .setIcon(android.R.drawable.ic_dialog_alert)
703                    .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
704                    .setPositiveButton(R.string.dlg_ok,
705                        new DialogInterface.OnClickListener() {
706                        public void onClick(DialogInterface dialog, int which) {
707                            // Force stop
708                            getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName);
709                        }
710                    })
711                    .setNegativeButton(R.string.dlg_cancel, null)
712                    .create();
713                case DLG_MOVE_FAILED:
714                    CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text,
715                            getOwner().getMoveErrMsg(moveErrorCode));
716                    return new AlertDialog.Builder(getActivity())
717                    .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title))
718                    .setIcon(android.R.drawable.ic_dialog_alert)
719                    .setMessage(msg)
720                    .setNeutralButton(R.string.dlg_ok, null)
721                    .create();
722            }
723            throw new IllegalArgumentException("unknown id " + id);
724        }
725    }
726
727    private void uninstallPkg(String packageName) {
728         // Create new intent to launch Uninstaller activity
729        Uri packageURI = Uri.parse("package:"+packageName);
730        Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
731        startActivity(uninstallIntent);
732        setIntentAndFinish(true, true);
733    }
734
735    private void forceStopPackage(String pkgName) {
736        ActivityManager am = (ActivityManager)getActivity().getSystemService(
737                Context.ACTIVITY_SERVICE);
738        am.forceStopPackage(pkgName);
739        mState.invalidatePackage(pkgName);
740        ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName);
741        if (newEnt != null) {
742            mAppEntry = newEnt;
743        }
744        checkForceStop();
745    }
746
747    private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
748        @Override
749        public void onReceive(Context context, Intent intent) {
750            updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED);
751        }
752    };
753
754    private void updateForceStopButton(boolean enabled) {
755        mForceStopButton.setEnabled(enabled);
756        mForceStopButton.setOnClickListener(InstalledAppDetails.this);
757    }
758
759    private void checkForceStop() {
760        Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
761                Uri.fromParts("package", mAppEntry.info.packageName, null));
762        intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
763        intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
764        if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
765            // If the app isn't explicitly stopped, then always show the
766            // force stop button.
767            updateForceStopButton(true);
768        } else {
769            getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
770                    Activity.RESULT_CANCELED, null, null);
771        }
772    }
773
774    static class DisableChanger extends AsyncTask<Object, Object, Object> {
775        final PackageManager mPm;
776        final WeakReference<InstalledAppDetails> mActivity;
777        final ApplicationInfo mInfo;
778        final int mState;
779
780        DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
781            mPm = activity.mPm;
782            mActivity = new WeakReference<InstalledAppDetails>(activity);
783            mInfo = info;
784            mState = state;
785        }
786
787        @Override
788        protected Object doInBackground(Object... params) {
789            mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
790            return null;
791        }
792    }
793
794    /*
795     * Method implementing functionality of buttons clicked
796     * @see android.view.View.OnClickListener#onClick(android.view.View)
797     */
798    public void onClick(View v) {
799        String packageName = mAppEntry.info.packageName;
800        if(v == mUninstallButton) {
801            if (mUpdatedSysApp) {
802                showDialogInner(DLG_FACTORY_RESET, 0);
803            } else {
804                if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
805                    new DisableChanger(this, mAppEntry.info, mAppEntry.info.enabled ?
806                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED
807                            : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).execute((Object)null);
808                } else {
809                    uninstallPkg(packageName);
810                }
811            }
812        } else if(v == mActivitiesButton) {
813            mPm.clearPackagePreferredActivities(packageName);
814            try {
815                mUsbManager.clearDefaults(packageName);
816            } catch (RemoteException e) {
817                Log.e(TAG, "mUsbManager.clearDefaults", e);
818            }
819            mActivitiesButton.setEnabled(false);
820        } else if(v == mClearDataButton) {
821            if (mAppEntry.info.manageSpaceActivityName != null) {
822                Intent intent = new Intent(Intent.ACTION_DEFAULT);
823                intent.setClassName(mAppEntry.info.packageName,
824                        mAppEntry.info.manageSpaceActivityName);
825                startActivityForResult(intent, -1);
826            } else {
827                showDialogInner(DLG_CLEAR_DATA, 0);
828            }
829        } else if (v == mClearCacheButton) {
830            // Lazy initialization of observer
831            if (mClearCacheObserver == null) {
832                mClearCacheObserver = new ClearCacheObserver();
833            }
834            mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
835        } else if (v == mForceStopButton) {
836            showDialogInner(DLG_FORCE_STOP, 0);
837            //forceStopPackage(mAppInfo.packageName);
838        } else if (v == mMoveAppButton) {
839            if (mPackageMoveObserver == null) {
840                mPackageMoveObserver = new PackageMoveObserver();
841            }
842            int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
843                    PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA;
844            mMoveInProgress = true;
845            refreshButtons();
846            mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags);
847        }
848    }
849}
850
851