OSDN Git Service

am 4fedc0fd: (-s ours) Import translations. DO NOT MERGE
[android-x86/packages-apps-Settings.git] / src / com / android / settings / applications / InstalledAppDetails.java
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
17 package com.android.settings.applications;
18
19 import com.android.internal.telephony.ISms;
20 import com.android.internal.telephony.SmsUsageMonitor;
21 import com.android.settings.R;
22 import com.android.settings.SettingsActivity;
23 import com.android.settings.Utils;
24 import com.android.settings.applications.ApplicationsState.AppEntry;
25
26 import android.app.Activity;
27 import android.app.ActivityManager;
28 import android.app.AlertDialog;
29 import android.app.Dialog;
30 import android.app.DialogFragment;
31 import android.app.Fragment;
32 import android.app.INotificationManager;
33 import android.app.admin.DevicePolicyManager;
34 import android.appwidget.AppWidgetManager;
35 import android.content.BroadcastReceiver;
36 import android.content.ComponentName;
37 import android.content.Context;
38 import android.content.DialogInterface;
39 import android.content.Intent;
40 import android.content.IntentFilter;
41 import android.content.pm.ApplicationInfo;
42 import android.content.pm.IPackageDataObserver;
43 import android.content.pm.IPackageMoveObserver;
44 import android.content.pm.PackageInfo;
45 import android.content.pm.PackageManager;
46 import android.content.pm.ResolveInfo;
47 import android.content.pm.PackageManager.NameNotFoundException;
48 import android.content.res.Resources;
49 import android.hardware.usb.IUsbManager;
50 import android.net.Uri;
51 import android.os.AsyncTask;
52 import android.os.Bundle;
53 import android.os.Environment;
54 import android.os.Handler;
55 import android.os.IBinder;
56 import android.os.Message;
57 import android.os.RemoteException;
58 import android.os.ServiceManager;
59 import android.os.UserHandle;
60 import android.os.UserManager;
61 import android.text.SpannableString;
62 import android.text.TextUtils;
63 import android.text.format.Formatter;
64 import android.text.style.BulletSpan;
65 import android.util.Log;
66
67 import java.lang.ref.WeakReference;
68 import java.util.ArrayList;
69 import java.util.HashSet;
70 import java.util.List;
71 import android.view.LayoutInflater;
72 import android.view.Menu;
73 import android.view.MenuInflater;
74 import android.view.MenuItem;
75 import android.view.View;
76 import android.view.ViewGroup;
77 import android.widget.AdapterView;
78 import android.widget.AppSecurityPermissions;
79 import android.widget.ArrayAdapter;
80 import android.widget.Button;
81 import android.widget.CheckBox;
82 import android.widget.CompoundButton;
83 import android.widget.ImageView;
84 import android.widget.LinearLayout;
85 import android.widget.Spinner;
86 import android.widget.TextView;
87
88 /**
89  * Activity to display application information from Settings. This activity presents
90  * extended information associated with a package like code, data, total size, permissions
91  * used by the application and also the set of default launchable activities.
92  * For system applications, an option to clear user data is displayed only if data size is > 0.
93  * System applications that do not want clear user data do not have this option.
94  * For non-system applications, there is no option to clear data. Instead there is an option to
95  * uninstall the application.
96  */
97 public class InstalledAppDetails extends Fragment
98         implements View.OnClickListener, CompoundButton.OnCheckedChangeListener,
99         ApplicationsState.Callbacks {
100     private static final String TAG="InstalledAppDetails";
101     private static final boolean localLOGV = false;
102     
103     public static final String ARG_PACKAGE_NAME = "package";
104
105     private PackageManager mPm;
106     private UserManager mUserManager;
107     private IUsbManager mUsbManager;
108     private AppWidgetManager mAppWidgetManager;
109     private DevicePolicyManager mDpm;
110     private ISms mSmsManager;
111     private ApplicationsState mState;
112     private ApplicationsState.Session mSession;
113     private ApplicationsState.AppEntry mAppEntry;
114     private boolean mInitialized;
115     private boolean mShowUninstalled;
116     private PackageInfo mPackageInfo;
117     private CanBeOnSdCardChecker mCanBeOnSdCardChecker;
118     private View mRootView;
119     private Button mUninstallButton;
120     private View mMoreControlButtons;
121     private Button mSpecialDisableButton;
122     private boolean mMoveInProgress = false;
123     private boolean mUpdatedSysApp = false;
124     private Button mActivitiesButton;
125     private View mScreenCompatSection;
126     private CheckBox mAskCompatibilityCB;
127     private CheckBox mEnableCompatibilityCB;
128     private boolean mCanClearData = true;
129     private boolean mAppControlRestricted = false;
130     private TextView mAppVersion;
131     private TextView mTotalSize;
132     private TextView mAppSize;
133     private TextView mDataSize;
134     private TextView mExternalCodeSize;
135     private TextView mExternalDataSize;
136     private ClearUserDataObserver mClearDataObserver;
137     // Views related to cache info
138     private TextView mCacheSize;
139     private Button mClearCacheButton;
140     private ClearCacheObserver mClearCacheObserver;
141     private Button mForceStopButton;
142     private Button mClearDataButton;
143     private Button mMoveAppButton;
144     private CompoundButton mNotificationSwitch;
145
146     private PackageMoveObserver mPackageMoveObserver;
147
148     private final HashSet<String> mHomePackages = new HashSet<String>();
149
150     private boolean mDisableAfterUninstall;
151
152     private boolean mHaveSizes = false;
153     private long mLastCodeSize = -1;
154     private long mLastDataSize = -1;
155     private long mLastExternalCodeSize = -1;
156     private long mLastExternalDataSize = -1;
157     private long mLastCacheSize = -1;
158     private long mLastTotalSize = -1;
159     
160     //internal constants used in Handler
161     private static final int OP_SUCCESSFUL = 1;
162     private static final int OP_FAILED = 2;
163     private static final int CLEAR_USER_DATA = 1;
164     private static final int CLEAR_CACHE = 3;
165     private static final int PACKAGE_MOVE = 4;
166     
167     // invalid size value used initially and also when size retrieval through PackageManager
168     // fails for whatever reason
169     private static final int SIZE_INVALID = -1;
170     
171     // Resource strings
172     private CharSequence mInvalidSizeStr;
173     private CharSequence mComputingStr;
174     
175     // Dialog identifiers used in showDialog
176     private static final int DLG_BASE = 0;
177     private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
178     private static final int DLG_FACTORY_RESET = DLG_BASE + 2;
179     private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3;
180     private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4;
181     private static final int DLG_FORCE_STOP = DLG_BASE + 5;
182     private static final int DLG_MOVE_FAILED = DLG_BASE + 6;
183     private static final int DLG_DISABLE = DLG_BASE + 7;
184     private static final int DLG_DISABLE_NOTIFICATIONS = DLG_BASE + 8;
185     private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 9;
186
187     // Menu identifiers
188     public static final int UNINSTALL_ALL_USERS_MENU = 1;
189
190     // Result code identifiers
191     public static final int REQUEST_UNINSTALL = 1;
192     public static final int REQUEST_MANAGE_SPACE = 2;
193
194     private Handler mHandler = new Handler() {
195         public void handleMessage(Message msg) {
196             // If the fragment is gone, don't process any more messages.
197             if (getView() == null) {
198                 return;
199             }
200             switch (msg.what) {
201                 case CLEAR_USER_DATA:
202                     processClearMsg(msg);
203                     break;
204                 case CLEAR_CACHE:
205                     // Refresh size info
206                     mState.requestSize(mAppEntry.info.packageName);
207                     break;
208                 case PACKAGE_MOVE:
209                     processMoveMsg(msg);
210                     break;
211                 default:
212                     break;
213             }
214         }
215     };
216     
217     class ClearUserDataObserver extends IPackageDataObserver.Stub {
218        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
219            final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
220            msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
221            mHandler.sendMessage(msg);
222         }
223     }
224     
225     class ClearCacheObserver extends IPackageDataObserver.Stub {
226         public void onRemoveCompleted(final String packageName, final boolean succeeded) {
227             final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
228             msg.arg1 = succeeded ? OP_SUCCESSFUL:OP_FAILED;
229             mHandler.sendMessage(msg);
230          }
231      }
232
233     class PackageMoveObserver extends IPackageMoveObserver.Stub {
234         public void packageMoved(String packageName, int returnCode) throws RemoteException {
235             final Message msg = mHandler.obtainMessage(PACKAGE_MOVE);
236             msg.arg1 = returnCode;
237             mHandler.sendMessage(msg);
238         }
239     }
240     
241     private String getSizeStr(long size) {
242         if (size == SIZE_INVALID) {
243             return mInvalidSizeStr.toString();
244         }
245         return Formatter.formatFileSize(getActivity(), size);
246     }
247     
248     private void initDataButtons() {
249         // If the app doesn't have its own space management UI
250         // And it's a system app that doesn't allow clearing user data or is an active admin
251         // Then disable the Clear Data button.
252         if (mAppEntry.info.manageSpaceActivityName == null
253                 && ((mAppEntry.info.flags&(ApplicationInfo.FLAG_SYSTEM
254                         | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA))
255                         == ApplicationInfo.FLAG_SYSTEM
256                         || mDpm.packageHasActiveAdmins(mPackageInfo.packageName))) {
257             mClearDataButton.setText(R.string.clear_user_data_text);
258             mClearDataButton.setEnabled(false);
259             mCanClearData = false;
260         } else {
261             if (mAppEntry.info.manageSpaceActivityName != null) {
262                 mClearDataButton.setText(R.string.manage_space_text);
263             } else {
264                 mClearDataButton.setText(R.string.clear_user_data_text);
265             }
266             mClearDataButton.setOnClickListener(this);
267         }
268
269         if (mAppControlRestricted) {
270             mClearDataButton.setEnabled(false);
271         }
272     }
273
274     private CharSequence getMoveErrMsg(int errCode) {
275         switch (errCode) {
276             case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE:
277                 return getActivity().getString(R.string.insufficient_storage);
278             case PackageManager.MOVE_FAILED_DOESNT_EXIST:
279                 return getActivity().getString(R.string.does_not_exist);
280             case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
281                 return getActivity().getString(R.string.app_forward_locked);
282             case PackageManager.MOVE_FAILED_INVALID_LOCATION:
283                 return getActivity().getString(R.string.invalid_location);
284             case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
285                 return getActivity().getString(R.string.system_package);
286             case PackageManager.MOVE_FAILED_INTERNAL_ERROR:
287                 return "";
288         }
289         return "";
290     }
291
292     private void initMoveButton() {
293         if (Environment.isExternalStorageEmulated()) {
294             mMoveAppButton.setVisibility(View.INVISIBLE);
295             return;
296         }
297         boolean dataOnly = false;
298         dataOnly = (mPackageInfo == null) && (mAppEntry != null);
299         boolean moveDisable = true;
300         if (dataOnly) {
301             mMoveAppButton.setText(R.string.move_app);
302         } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
303             mMoveAppButton.setText(R.string.move_app_to_internal);
304             // Always let apps move to internal storage from sdcard.
305             moveDisable = false;
306         } else {
307             mMoveAppButton.setText(R.string.move_app_to_sdcard);
308             mCanBeOnSdCardChecker.init();
309             moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info);
310         }
311         if (moveDisable || mAppControlRestricted) {
312             mMoveAppButton.setEnabled(false);
313         } else {
314             mMoveAppButton.setOnClickListener(this);
315             mMoveAppButton.setEnabled(true);
316         }
317     }
318
319     private boolean handleDisableable(Button button) {
320         boolean disableable = false;
321         // Try to prevent the user from bricking their phone
322         // by not allowing disabling of apps signed with the
323         // system cert and any launcher app in the system.
324         if (mHomePackages.contains(mAppEntry.info.packageName)
325                 || Utils.isSystemPackage(mPm, mPackageInfo)) {
326             // Disable button for core system applications.
327             button.setText(R.string.disable_text);
328         } else if (mAppEntry.info.enabled) {
329             button.setText(R.string.disable_text);
330             disableable = true;
331         } else {
332             button.setText(R.string.enable_text);
333             disableable = true;
334         }
335
336         return disableable;
337     }
338
339     private void initUninstallButtons() {
340         mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
341         final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
342         boolean enabled = true;
343         if (mUpdatedSysApp) {
344             mUninstallButton.setText(R.string.app_factory_reset);
345             boolean showSpecialDisable = false;
346             if (isBundled) {
347                 showSpecialDisable = handleDisableable(mSpecialDisableButton);
348                 mSpecialDisableButton.setOnClickListener(this);
349             }
350             if (mAppControlRestricted) {
351                 showSpecialDisable = false;
352             }
353             mMoreControlButtons.setVisibility(showSpecialDisable ? View.VISIBLE : View.GONE);
354         } else {
355             mMoreControlButtons.setVisibility(View.GONE);
356             if (isBundled) {
357                 enabled = handleDisableable(mUninstallButton);
358             } else if ((mPackageInfo.applicationInfo.flags
359                     & ApplicationInfo.FLAG_INSTALLED) == 0
360                     && mUserManager.getUsers().size() >= 2) {
361                 // When we have multiple users, there is a separate menu
362                 // to uninstall for all users.
363                 mUninstallButton.setText(R.string.uninstall_text);
364                 enabled = false;
365             } else {
366                 mUninstallButton.setText(R.string.uninstall_text);
367             }
368         }
369         // If this is a device admin, it can't be uninstalled or disabled.
370         // We do this here so the text of the button is still set correctly.
371         if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
372             enabled = false;
373         }
374
375         // Home apps need special handling.  Bundled ones we don't risk downgrading
376         // because that can interfere with home-key resolution.  Furthermore, we
377         // can't allow uninstallation of the only home app, and we don't want to
378         // allow uninstallation of an explicitly preferred one -- the user can go
379         // to Home settings and pick a different one, after which we'll permit
380         // uninstallation of the now-not-default one.
381         if (enabled && mHomePackages.contains(mPackageInfo.packageName)) {
382             if (isBundled) {
383                 enabled = false;
384             } else {
385                 ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
386                 ComponentName currentDefaultHome  = mPm.getHomeActivities(homeActivities);
387                 if (currentDefaultHome == null) {
388                     // No preferred default, so permit uninstall only when
389                     // there is more than one candidate
390                     enabled = (mHomePackages.size() > 1);
391                 } else {
392                     // There is an explicit default home app -- forbid uninstall of
393                     // that one, but permit it for installed-but-inactive ones.
394                     enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
395                 }
396             }
397         }
398
399         if (mAppControlRestricted) {
400             enabled = false;
401         }
402
403         mUninstallButton.setEnabled(enabled);
404         if (enabled) {
405             // Register listener
406             mUninstallButton.setOnClickListener(this);
407         }
408     }
409
410     private void initNotificationButton() {
411         INotificationManager nm = INotificationManager.Stub.asInterface(
412                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
413         boolean enabled = true; // default on
414         try {
415             enabled = nm.areNotificationsEnabledForPackage(mAppEntry.info.packageName,
416                     mAppEntry.info.uid);
417         } catch (android.os.RemoteException ex) {
418             // this does not bode well
419         }
420         mNotificationSwitch.setChecked(enabled);
421         if (Utils.isSystemPackage(mPm, mPackageInfo)) {
422             mNotificationSwitch.setEnabled(false);
423         } else if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
424             // App is not installed on the current user
425             mNotificationSwitch.setEnabled(false);
426         } else {
427             mNotificationSwitch.setEnabled(true);
428             mNotificationSwitch.setOnCheckedChangeListener(this);
429         }
430     }
431
432     /** Called when the activity is first created. */
433     @Override
434     public void onCreate(Bundle icicle) {
435         super.onCreate(icicle);
436
437         mState = ApplicationsState.getInstance(getActivity().getApplication());
438         mSession = mState.newSession(this);
439         mPm = getActivity().getPackageManager();
440         mUserManager = (UserManager)getActivity().getSystemService(Context.USER_SERVICE);
441         IBinder b = ServiceManager.getService(Context.USB_SERVICE);
442         mUsbManager = IUsbManager.Stub.asInterface(b);
443         mAppWidgetManager = AppWidgetManager.getInstance(getActivity());
444         mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
445         mSmsManager = ISms.Stub.asInterface(ServiceManager.getService("isms"));
446
447         mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
448
449         // Need to make sure we have loaded applications at this point.
450         mSession.resume();
451
452         retrieveAppEntry();
453
454         setHasOptionsMenu(true);
455     }
456
457     @Override
458     public View onCreateView(
459             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
460         final View view = inflater.inflate(R.layout.installed_app_details, container, false);
461
462         final ViewGroup allDetails = (ViewGroup) view.findViewById(R.id.all_details);
463         Utils.forceCustomPadding(allDetails, true /* additive padding */);
464
465         mRootView = view;
466         mComputingStr = getActivity().getText(R.string.computing_size);
467         
468         // Set default values on sizes
469         mTotalSize = (TextView) view.findViewById(R.id.total_size_text);
470         mAppSize = (TextView) view.findViewById(R.id.application_size_text);
471         mDataSize = (TextView) view.findViewById(R.id.data_size_text);
472         mExternalCodeSize = (TextView) view.findViewById(R.id.external_code_size_text);
473         mExternalDataSize = (TextView) view.findViewById(R.id.external_data_size_text);
474
475         if (Environment.isExternalStorageEmulated()) {
476             ((View)mExternalCodeSize.getParent()).setVisibility(View.GONE);
477             ((View)mExternalDataSize.getParent()).setVisibility(View.GONE);
478         }
479
480         // Get Control button panel
481         View btnPanel = view.findViewById(R.id.control_buttons_panel);
482         mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button);
483         mForceStopButton.setText(R.string.force_stop);
484         mUninstallButton = (Button) btnPanel.findViewById(R.id.right_button);
485         mForceStopButton.setEnabled(false);
486         
487         // Get More Control button panel
488         mMoreControlButtons = view.findViewById(R.id.more_control_buttons_panel);
489         mMoreControlButtons.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
490         mSpecialDisableButton = (Button) mMoreControlButtons.findViewById(R.id.right_button);
491         mMoreControlButtons.setVisibility(View.GONE);
492         
493         // Initialize clear data and move install location buttons
494         View data_buttons_panel = view.findViewById(R.id.data_buttons_panel);
495         mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.right_button);
496         mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.left_button);
497         
498         // Cache section
499         mCacheSize = (TextView) view.findViewById(R.id.cache_size_text);
500         mClearCacheButton = (Button) view.findViewById(R.id.clear_cache_button);
501
502         mActivitiesButton = (Button) view.findViewById(R.id.clear_activities_button);
503         
504         // Screen compatibility control
505         mScreenCompatSection = view.findViewById(R.id.screen_compatibility_section);
506         mAskCompatibilityCB = (CheckBox) view.findViewById(R.id.ask_compatibility_cb);
507         mEnableCompatibilityCB = (CheckBox) view.findViewById(R.id.enable_compatibility_cb);
508         
509         mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch);
510
511         return view;
512     }
513
514     @Override
515     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
516         menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
517                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
518     }
519
520     @Override
521     public void onPrepareOptionsMenu(Menu menu) {
522         boolean showIt = true;
523         if (mUpdatedSysApp) {
524             showIt = false;
525         } else if (mAppEntry == null) {
526             showIt = false;
527         } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
528             showIt = false;
529         } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
530             showIt = false;
531         } else if (UserHandle.myUserId() != 0) {
532             showIt = false;
533         } else if (mUserManager.getUsers().size() < 2) {
534             showIt = false;
535         }
536         menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt);
537     }
538
539     @Override
540     public boolean onOptionsItemSelected(MenuItem item) {
541         int menuId = item.getItemId();
542         if (menuId == UNINSTALL_ALL_USERS_MENU) {
543             uninstallPkg(mAppEntry.info.packageName, true, false);
544             return true;
545         }
546         return false;
547     }
548
549     @Override
550     public void onActivityResult(int requestCode, int resultCode, Intent data) {
551         super.onActivityResult(requestCode, resultCode, data);
552         if (requestCode == REQUEST_UNINSTALL) {
553             if (mDisableAfterUninstall) {
554                 mDisableAfterUninstall = false;
555                 try {
556                     ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
557                             mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES
558                             | PackageManager.GET_DISABLED_COMPONENTS);
559                     if ((ainfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
560                         new DisableChanger(this, mAppEntry.info,
561                                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
562                                 .execute((Object)null);
563                     }
564                 } catch (NameNotFoundException e) {
565                 }
566             }
567             if (!refreshUi()) {
568                 setIntentAndFinish(true, true);
569             }
570         }
571     }
572
573     // Utility method to set application label and icon.
574     private void setAppLabelAndIcon(PackageInfo pkgInfo) {
575         final View appSnippet = mRootView.findViewById(R.id.app_snippet);
576         appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, appSnippet.getPaddingBottom());
577
578         ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon);
579         mState.ensureIcon(mAppEntry);
580         icon.setImageDrawable(mAppEntry.icon);
581         // Set application name.
582         TextView label = (TextView) appSnippet.findViewById(R.id.app_name);
583         label.setText(mAppEntry.label);
584         // Version number of application
585         mAppVersion = (TextView) appSnippet.findViewById(R.id.app_size);
586
587         if (pkgInfo != null && pkgInfo.versionName != null) {
588             mAppVersion.setVisibility(View.VISIBLE);
589             mAppVersion.setText(getActivity().getString(R.string.version_text,
590                     String.valueOf(pkgInfo.versionName)));
591         } else {
592             mAppVersion.setVisibility(View.INVISIBLE);
593         }
594     }
595
596     @Override
597     public void onResume() {
598         super.onResume();
599         
600         mAppControlRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL);
601         mSession.resume();
602         if (!refreshUi()) {
603             setIntentAndFinish(true, true);
604         }
605     }
606
607     @Override
608     public void onPause() {
609         super.onPause();
610         mSession.pause();
611     }
612
613     @Override
614     public void onDestroyView() {
615         super.onDestroyView();
616         mSession.release();
617     }
618
619     @Override
620     public void onAllSizesComputed() {
621     }
622
623     @Override
624     public void onPackageIconChanged() {
625     }
626
627     @Override
628     public void onPackageListChanged() {
629         refreshUi();
630     }
631
632     @Override
633     public void onRebuildComplete(ArrayList<AppEntry> apps) {
634     }
635
636     @Override
637     public void onPackageSizeChanged(String packageName) {
638         if (packageName.equals(mAppEntry.info.packageName)) {
639             refreshSizeInfo();
640         }
641     }
642
643     @Override
644     public void onRunningStateChanged(boolean running) {
645     }
646
647     private String retrieveAppEntry() {
648         final Bundle args = getArguments();
649         String packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
650         if (packageName == null) {
651             Intent intent = (args == null) ?
652                     getActivity().getIntent() : (Intent) args.getParcelable("intent");
653             if (intent != null) {
654                 packageName = intent.getData().getSchemeSpecificPart();
655             }
656         }
657         mAppEntry = mState.getEntry(packageName);
658         if (mAppEntry != null) {
659             // Get application info again to refresh changed properties of application
660             try {
661                 mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
662                         PackageManager.GET_DISABLED_COMPONENTS |
663                         PackageManager.GET_UNINSTALLED_PACKAGES |
664                         PackageManager.GET_SIGNATURES);
665             } catch (NameNotFoundException e) {
666                 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
667             }
668         } else {
669             Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
670             mPackageInfo = null;
671         }
672
673         return packageName;
674     }
675
676     private boolean signaturesMatch(String pkg1, String pkg2) {
677         if (pkg1 != null && pkg2 != null) {
678             try {
679                 final int match = mPm.checkSignatures(pkg1, pkg2);
680                 if (match >= PackageManager.SIGNATURE_MATCH) {
681                     return true;
682                 }
683             } catch (Exception e) {
684                 // e.g. named alternate package not found during lookup;
685                 // this is an expected case sometimes
686             }
687         }
688         return false;
689     }
690
691     private boolean refreshUi() {
692         if (mMoveInProgress) {
693             return true;
694         }
695         final String packageName = retrieveAppEntry();
696
697         if (mAppEntry == null) {
698             return false; // onCreate must have failed, make sure to exit
699         }
700
701         if (mPackageInfo == null) {
702             return false; // onCreate must have failed, make sure to exit
703         }
704
705         // Get list of "home" apps and trace through any meta-data references
706         List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
707         mPm.getHomeActivities(homeActivities);
708         mHomePackages.clear();
709         for (int i = 0; i< homeActivities.size(); i++) {
710             ResolveInfo ri = homeActivities.get(i);
711             final String activityPkg = ri.activityInfo.packageName;
712             mHomePackages.add(activityPkg);
713
714             // Also make sure to include anything proxying for the home app
715             final Bundle metadata = ri.activityInfo.metaData;
716             if (metadata != null) {
717                 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
718                 if (signaturesMatch(metaPkg, activityPkg)) {
719                     mHomePackages.add(metaPkg);
720                 }
721             }
722         }
723
724         // Get list of preferred activities
725         List<ComponentName> prefActList = new ArrayList<ComponentName>();
726         
727         // Intent list cannot be null. so pass empty list
728         List<IntentFilter> intentList = new ArrayList<IntentFilter>();
729         mPm.getPreferredActivities(intentList, prefActList, packageName);
730         if (localLOGV)
731             Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
732         boolean hasUsbDefaults = false;
733         try {
734             if (mUsbManager != null) {
735                 hasUsbDefaults = mUsbManager.hasDefaults(packageName, UserHandle.myUserId());
736             }
737         } catch (RemoteException e) {
738             Log.e(TAG, "mUsbManager.hasDefaults", e);
739         }
740         boolean hasBindAppWidgetPermission =
741                 mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName);
742
743         TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title);
744         TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
745         boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults;
746         if (!autoLaunchEnabled && !hasBindAppWidgetPermission) {
747             resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
748         } else {
749             boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled;
750
751             if (hasBindAppWidgetPermission) {
752                 autoLaunchTitleView.setText(R.string.auto_launch_label_generic);
753             } else {
754                 autoLaunchTitleView.setText(R.string.auto_launch_label);
755             }
756
757             CharSequence text = null;
758             int bulletIndent = getResources()
759                     .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset);
760             if (autoLaunchEnabled) {
761                 CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text);
762                 SpannableString s = new SpannableString(autoLaunchEnableText);
763                 if (useBullets) {
764                     s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0);
765                 }
766                 text = (text == null) ?
767                         TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
768             }
769             if (hasBindAppWidgetPermission) {
770                 CharSequence alwaysAllowBindAppWidgetsText =
771                         getText(R.string.always_allow_bind_appwidgets_text);
772                 SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText);
773                 if (useBullets) {
774                     s.setSpan(new BulletSpan(bulletIndent),
775                             0, alwaysAllowBindAppWidgetsText.length(), 0);
776                 }
777                 text = (text == null) ?
778                         TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
779             }
780             autoLaunchView.setText(text);
781             mActivitiesButton.setEnabled(true);
782             mActivitiesButton.setOnClickListener(this);
783         }
784
785         // Screen compatibility section.
786         ActivityManager am = (ActivityManager)
787                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
788         int compatMode = am.getPackageScreenCompatMode(packageName);
789         // For now these are always off; this is the old UI model which we
790         // are no longer using.
791         if (false && (compatMode == ActivityManager.COMPAT_MODE_DISABLED
792                 || compatMode == ActivityManager.COMPAT_MODE_ENABLED)) {
793             mScreenCompatSection.setVisibility(View.VISIBLE);
794             mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName));
795             mAskCompatibilityCB.setOnCheckedChangeListener(this);
796             mEnableCompatibilityCB.setChecked(compatMode == ActivityManager.COMPAT_MODE_ENABLED);
797             mEnableCompatibilityCB.setOnCheckedChangeListener(this);
798         } else {
799             mScreenCompatSection.setVisibility(View.GONE);
800         }
801
802         // Security permissions section
803         LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section);
804         AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName);
805         int premiumSmsPermission = getPremiumSmsPermission(packageName);
806         // Premium SMS permission implies the app also has SEND_SMS permission, so the original
807         // application permissions list doesn't have to be shown/hidden separately. The premium
808         // SMS subsection should only be visible if the app has tried to send to a premium SMS.
809         if (asp.getPermissionCount() > 0
810                 || premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
811             permsView.setVisibility(View.VISIBLE);
812         } else {
813             permsView.setVisibility(View.GONE);
814         }
815         // Premium SMS permission subsection
816         TextView securityBillingDesc = (TextView) permsView.findViewById(
817                 R.id.security_settings_billing_desc);
818         LinearLayout securityBillingList = (LinearLayout) permsView.findViewById(
819                 R.id.security_settings_billing_list);
820         if (premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
821             // Show the premium SMS permission selector
822             securityBillingDesc.setVisibility(View.VISIBLE);
823             securityBillingList.setVisibility(View.VISIBLE);
824             Spinner spinner = (Spinner) permsView.findViewById(
825                     R.id.security_settings_premium_sms_list);
826             ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity(),
827                     R.array.security_settings_premium_sms_values,
828                     android.R.layout.simple_spinner_item);
829             adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
830             spinner.setAdapter(adapter);
831             // List items are in the same order as SmsUsageMonitor constants, offset by 1.
832             spinner.setSelection(premiumSmsPermission - 1);
833             spinner.setOnItemSelectedListener(new PremiumSmsSelectionListener(
834                     packageName, mSmsManager));
835         } else {
836             // Hide the premium SMS permission selector
837             securityBillingDesc.setVisibility(View.GONE);
838             securityBillingList.setVisibility(View.GONE);
839         }
840         // App permissions subsection
841         if (asp.getPermissionCount() > 0) {
842             // Make the security sections header visible
843             LinearLayout securityList = (LinearLayout) permsView.findViewById(
844                     R.id.security_settings_list);
845             securityList.removeAllViews();
846             securityList.addView(asp.getPermissionsViewWithRevokeButtons());
847             // If this app is running under a shared user ID with other apps,
848             // update the description to explain this.
849             String[] packages = mPm.getPackagesForUid(mPackageInfo.applicationInfo.uid);
850             if (packages != null && packages.length > 1) {
851                 ArrayList<CharSequence> pnames = new ArrayList<CharSequence>();
852                 for (int i=0; i<packages.length; i++) {
853                     String pkg = packages[i];
854                     if (mPackageInfo.packageName.equals(pkg)) {
855                         continue;
856                     }
857                     try {
858                         ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0);
859                         pnames.add(ainfo.loadLabel(mPm));
860                     } catch (PackageManager.NameNotFoundException e) {
861                     }
862                 }
863                 final int N = pnames.size();
864                 if (N > 0) {
865                     final Resources res = getActivity().getResources();
866                     String appListStr;
867                     if (N == 1) {
868                         appListStr = pnames.get(0).toString();
869                     } else if (N == 2) {
870                         appListStr = res.getString(R.string.join_two_items, pnames.get(0),
871                                 pnames.get(1));
872                     } else {
873                         appListStr = pnames.get(N-2).toString();
874                         for (int i=N-3; i>=0; i--) {
875                             appListStr = res.getString(i == 0 ? R.string.join_many_items_first
876                                     : R.string.join_many_items_middle, pnames.get(i), appListStr);
877                         }
878                         appListStr = res.getString(R.string.join_many_items_last,
879                                 appListStr, pnames.get(N-1));
880                     }
881                     TextView descr = (TextView) mRootView.findViewById(
882                             R.id.security_settings_desc);
883                     descr.setText(res.getString(R.string.security_settings_desc_multi,
884                             mPackageInfo.applicationInfo.loadLabel(mPm), appListStr));
885                 }
886             }
887         }
888         
889         checkForceStop();
890         setAppLabelAndIcon(mPackageInfo);
891         refreshButtons();
892         refreshSizeInfo();
893
894         if (!mInitialized) {
895             // First time init: are we displaying an uninstalled app?
896             mInitialized = true;
897             mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0;
898         } else {
899             // All other times: if the app no longer exists then we want
900             // to go away.
901             try {
902                 ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
903                         mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES
904                         | PackageManager.GET_DISABLED_COMPONENTS);
905                 if (!mShowUninstalled) {
906                     // If we did not start out with the app uninstalled, then
907                     // it transitioning to the uninstalled state for the current
908                     // user means we should go away as well.
909                     return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
910                 }
911             } catch (NameNotFoundException e) {
912                 return false;
913             }
914         }
915
916         return true;
917     }
918
919     private static class PremiumSmsSelectionListener implements AdapterView.OnItemSelectedListener {
920         private final String mPackageName;
921         private final ISms mSmsManager;
922
923         PremiumSmsSelectionListener(String packageName, ISms smsManager) {
924             mPackageName = packageName;
925             mSmsManager = smsManager;
926         }
927
928         @Override
929         public void onItemSelected(AdapterView<?> parent, View view, int position,
930                 long id) {
931             if (position >= 0 && position < 3) {
932                 Log.d(TAG, "Selected premium SMS policy " + position);
933                 setPremiumSmsPermission(mPackageName, (position + 1));
934             } else {
935                 Log.e(TAG, "Error: unknown premium SMS policy " + position);
936             }
937         }
938
939         @Override
940         public void onNothingSelected(AdapterView<?> parent) {
941             // Ignored
942         }
943
944         private void setPremiumSmsPermission(String packageName, int permission) {
945             try {
946                 if (mSmsManager != null) {
947                     mSmsManager.setPremiumSmsPermission(packageName, permission);
948                 }
949             } catch (RemoteException ex) {
950                 // ignored
951             }
952         }
953     }
954     
955     private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) {
956         title.setText(R.string.auto_launch_label);
957         autoLaunchView.setText(R.string.auto_launch_disable_text);
958         // Disable clear activities button
959         mActivitiesButton.setEnabled(false);
960     }
961
962     private void setIntentAndFinish(boolean finish, boolean appChanged) {
963         if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
964         Intent intent = new Intent();
965         intent.putExtra(ManageApplications.APP_CHG, appChanged);
966         SettingsActivity sa = (SettingsActivity)getActivity();
967         sa.finishPreferencePanel(this, Activity.RESULT_OK, intent);
968     }
969     
970     private void refreshSizeInfo() {
971         if (mAppEntry.size == ApplicationsState.SIZE_INVALID
972                 || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
973             mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
974             if (!mHaveSizes) {
975                 mAppSize.setText(mComputingStr);
976                 mDataSize.setText(mComputingStr);
977                 mCacheSize.setText(mComputingStr);
978                 mTotalSize.setText(mComputingStr);
979             }
980             mClearDataButton.setEnabled(false);
981             mClearCacheButton.setEnabled(false);
982             
983         } else {
984             mHaveSizes = true;
985             long codeSize = mAppEntry.codeSize;
986             long dataSize = mAppEntry.dataSize;
987             if (Environment.isExternalStorageEmulated()) {
988                 codeSize += mAppEntry.externalCodeSize;
989                 dataSize +=  mAppEntry.externalDataSize;
990             } else {
991                 if (mLastExternalCodeSize != mAppEntry.externalCodeSize) {
992                     mLastExternalCodeSize = mAppEntry.externalCodeSize;
993                     mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize));
994                 }
995                 if (mLastExternalDataSize !=  mAppEntry.externalDataSize) {
996                     mLastExternalDataSize =  mAppEntry.externalDataSize;
997                     mExternalDataSize.setText(getSizeStr( mAppEntry.externalDataSize));
998                 }
999             }
1000             if (mLastCodeSize != codeSize) {
1001                 mLastCodeSize = codeSize;
1002                 mAppSize.setText(getSizeStr(codeSize));
1003             }
1004             if (mLastDataSize != dataSize) {
1005                 mLastDataSize = dataSize;
1006                 mDataSize.setText(getSizeStr(dataSize));
1007             }
1008             long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize;
1009             if (mLastCacheSize != cacheSize) {
1010                 mLastCacheSize = cacheSize;
1011                 mCacheSize.setText(getSizeStr(cacheSize));
1012             }
1013             if (mLastTotalSize != mAppEntry.size) {
1014                 mLastTotalSize = mAppEntry.size;
1015                 mTotalSize.setText(getSizeStr(mAppEntry.size));
1016             }
1017             
1018             if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) {
1019                 mClearDataButton.setEnabled(false);
1020             } else {
1021                 mClearDataButton.setEnabled(true);
1022                 mClearDataButton.setOnClickListener(this);
1023             }
1024             if (cacheSize <= 0) {
1025                 mClearCacheButton.setEnabled(false);
1026             } else {
1027                 mClearCacheButton.setEnabled(true);
1028                 mClearCacheButton.setOnClickListener(this);
1029             }
1030         }
1031         if (mAppControlRestricted) {
1032             mClearCacheButton.setEnabled(false);
1033             mClearDataButton.setEnabled(false);
1034         }
1035     }
1036     
1037     /*
1038      * Private method to handle clear message notification from observer when
1039      * the async operation from PackageManager is complete
1040      */
1041     private void processClearMsg(Message msg) {
1042         int result = msg.arg1;
1043         String packageName = mAppEntry.info.packageName;
1044         mClearDataButton.setText(R.string.clear_user_data_text);
1045         if(result == OP_SUCCESSFUL) {
1046             Log.i(TAG, "Cleared user data for package : "+packageName);
1047             mState.requestSize(mAppEntry.info.packageName);
1048         } else {
1049             mClearDataButton.setEnabled(true);
1050         }
1051         checkForceStop();
1052     }
1053
1054     private void refreshButtons() {
1055         if (!mMoveInProgress) {
1056             initUninstallButtons();
1057             initDataButtons();
1058             initMoveButton();
1059             initNotificationButton();
1060         } else {
1061             mMoveAppButton.setText(R.string.moving);
1062             mMoveAppButton.setEnabled(false);
1063             mUninstallButton.setEnabled(false);
1064             mSpecialDisableButton.setEnabled(false);
1065         }
1066     }
1067
1068     private void processMoveMsg(Message msg) {
1069         int result = msg.arg1;
1070         String packageName = mAppEntry.info.packageName;
1071         // Refresh the button attributes.
1072         mMoveInProgress = false;
1073         if (result == PackageManager.MOVE_SUCCEEDED) {
1074             Log.i(TAG, "Moved resources for " + packageName);
1075             // Refresh size information again.
1076             mState.requestSize(mAppEntry.info.packageName);
1077         } else {
1078             showDialogInner(DLG_MOVE_FAILED, result);
1079         }
1080         refreshUi();
1081     }
1082
1083     /*
1084      * Private method to initiate clearing user data when the user clicks the clear data 
1085      * button for a system package
1086      */
1087     private  void initiateClearUserData() {
1088         mClearDataButton.setEnabled(false);
1089         // Invoke uninstall or clear user data based on sysPackage
1090         String packageName = mAppEntry.info.packageName;
1091         Log.i(TAG, "Clearing user data for package : " + packageName);
1092         if (mClearDataObserver == null) {
1093             mClearDataObserver = new ClearUserDataObserver();
1094         }
1095         ActivityManager am = (ActivityManager)
1096                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
1097         boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
1098         if (!res) {
1099             // Clearing data failed for some obscure reason. Just log error for now
1100             Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
1101             showDialogInner(DLG_CANNOT_CLEAR_DATA, 0);
1102         } else {
1103             mClearDataButton.setText(R.string.recompute_size);
1104         }
1105     }
1106     
1107     private void showDialogInner(int id, int moveErrorCode) {
1108         DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode);
1109         newFragment.setTargetFragment(this, 0);
1110         newFragment.show(getFragmentManager(), "dialog " + id);
1111     }
1112     
1113     public static class MyAlertDialogFragment extends DialogFragment {
1114
1115         public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) {
1116             MyAlertDialogFragment frag = new MyAlertDialogFragment();
1117             Bundle args = new Bundle();
1118             args.putInt("id", id);
1119             args.putInt("moveError", moveErrorCode);
1120             frag.setArguments(args);
1121             return frag;
1122         }
1123
1124         InstalledAppDetails getOwner() {
1125             return (InstalledAppDetails)getTargetFragment();
1126         }
1127
1128         @Override
1129         public Dialog onCreateDialog(Bundle savedInstanceState) {
1130             int id = getArguments().getInt("id");
1131             int moveErrorCode = getArguments().getInt("moveError");
1132             switch (id) {
1133                 case DLG_CLEAR_DATA:
1134                     return new AlertDialog.Builder(getActivity())
1135                     .setTitle(getActivity().getText(R.string.clear_data_dlg_title))
1136                     .setMessage(getActivity().getText(R.string.clear_data_dlg_text))
1137                     .setPositiveButton(R.string.dlg_ok,
1138                             new DialogInterface.OnClickListener() {
1139                         public void onClick(DialogInterface dialog, int which) {
1140                             // Clear user data here
1141                             getOwner().initiateClearUserData();
1142                         }
1143                     })
1144                     .setNegativeButton(R.string.dlg_cancel, null)
1145                     .create();
1146                 case DLG_FACTORY_RESET:
1147                     return new AlertDialog.Builder(getActivity())
1148                     .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title))
1149                     .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text))
1150                     .setPositiveButton(R.string.dlg_ok,
1151                             new DialogInterface.OnClickListener() {
1152                         public void onClick(DialogInterface dialog, int which) {
1153                             // Clear user data here
1154                             getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName,
1155                                     false, false);
1156                         }
1157                     })
1158                     .setNegativeButton(R.string.dlg_cancel, null)
1159                     .create();
1160                 case DLG_APP_NOT_FOUND:
1161                     return new AlertDialog.Builder(getActivity())
1162                     .setTitle(getActivity().getText(R.string.app_not_found_dlg_title))
1163                     .setMessage(getActivity().getText(R.string.app_not_found_dlg_title))
1164                     .setNeutralButton(getActivity().getText(R.string.dlg_ok),
1165                             new DialogInterface.OnClickListener() {
1166                         public void onClick(DialogInterface dialog, int which) {
1167                             //force to recompute changed value
1168                             getOwner().setIntentAndFinish(true, true);
1169                         }
1170                     })
1171                     .create();
1172                 case DLG_CANNOT_CLEAR_DATA:
1173                     return new AlertDialog.Builder(getActivity())
1174                     .setTitle(getActivity().getText(R.string.clear_failed_dlg_title))
1175                     .setMessage(getActivity().getText(R.string.clear_failed_dlg_text))
1176                     .setNeutralButton(R.string.dlg_ok,
1177                             new DialogInterface.OnClickListener() {
1178                         public void onClick(DialogInterface dialog, int which) {
1179                             getOwner().mClearDataButton.setEnabled(false);
1180                             //force to recompute changed value
1181                             getOwner().setIntentAndFinish(false, false);
1182                         }
1183                     })
1184                     .create();
1185                 case DLG_FORCE_STOP:
1186                     return new AlertDialog.Builder(getActivity())
1187                     .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
1188                     .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
1189                     .setPositiveButton(R.string.dlg_ok,
1190                         new DialogInterface.OnClickListener() {
1191                         public void onClick(DialogInterface dialog, int which) {
1192                             // Force stop
1193                             getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName);
1194                         }
1195                     })
1196                     .setNegativeButton(R.string.dlg_cancel, null)
1197                     .create();
1198                 case DLG_MOVE_FAILED:
1199                     CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text,
1200                             getOwner().getMoveErrMsg(moveErrorCode));
1201                     return new AlertDialog.Builder(getActivity())
1202                     .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title))
1203                     .setMessage(msg)
1204                     .setNeutralButton(R.string.dlg_ok, null)
1205                     .create();
1206                 case DLG_DISABLE:
1207                     return new AlertDialog.Builder(getActivity())
1208                     .setTitle(getActivity().getText(R.string.app_disable_dlg_title))
1209                     .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
1210                     .setPositiveButton(R.string.dlg_ok,
1211                         new DialogInterface.OnClickListener() {
1212                         public void onClick(DialogInterface dialog, int which) {
1213                             // Disable the app
1214                             new DisableChanger(getOwner(), getOwner().mAppEntry.info,
1215                                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
1216                             .execute((Object)null);
1217                         }
1218                     })
1219                     .setNegativeButton(R.string.dlg_cancel, null)
1220                     .create();
1221                 case DLG_DISABLE_NOTIFICATIONS:
1222                     return new AlertDialog.Builder(getActivity())
1223                     .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title))
1224                     .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text))
1225                     .setPositiveButton(R.string.dlg_ok,
1226                         new DialogInterface.OnClickListener() {
1227                         public void onClick(DialogInterface dialog, int which) {
1228                             // Disable the package's notifications
1229                             getOwner().setNotificationsEnabled(false);
1230                         }
1231                     })
1232                     .setNegativeButton(R.string.dlg_cancel,
1233                         new DialogInterface.OnClickListener() {
1234                         public void onClick(DialogInterface dialog, int which) {
1235                             // Re-enable the checkbox
1236                             getOwner().mNotificationSwitch.setChecked(true);
1237                         }
1238                     })
1239                     .create();
1240                 case DLG_SPECIAL_DISABLE:
1241                     return new AlertDialog.Builder(getActivity())
1242                     .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title))
1243                     .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text))
1244                     .setPositiveButton(R.string.dlg_ok,
1245                             new DialogInterface.OnClickListener() {
1246                         public void onClick(DialogInterface dialog, int which) {
1247                             // Clear user data here
1248                             getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName,
1249                                     false, true);
1250                         }
1251                     })
1252                     .setNegativeButton(R.string.dlg_cancel, null)
1253                     .create();
1254             }
1255             throw new IllegalArgumentException("unknown id " + id);
1256         }
1257     }
1258
1259     private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
1260          // Create new intent to launch Uninstaller activity
1261         Uri packageURI = Uri.parse("package:"+packageName);
1262         Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
1263         uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
1264         startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
1265         mDisableAfterUninstall = andDisable;
1266     }
1267
1268     private void forceStopPackage(String pkgName) {
1269         ActivityManager am = (ActivityManager)getActivity().getSystemService(
1270                 Context.ACTIVITY_SERVICE);
1271         am.forceStopPackage(pkgName);
1272         mState.invalidatePackage(pkgName);
1273         ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName);
1274         if (newEnt != null) {
1275             mAppEntry = newEnt;
1276         }
1277         checkForceStop();
1278     }
1279
1280     private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
1281         @Override
1282         public void onReceive(Context context, Intent intent) {
1283             updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED);
1284         }
1285     };
1286
1287     private void updateForceStopButton(boolean enabled) {
1288         if (mAppControlRestricted) {
1289             mForceStopButton.setEnabled(false);
1290         } else {
1291             mForceStopButton.setEnabled(enabled);
1292             mForceStopButton.setOnClickListener(InstalledAppDetails.this);
1293         }
1294     }
1295     
1296     private void checkForceStop() {
1297         if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
1298             // User can't force stop device admin.
1299             updateForceStopButton(false);
1300         } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
1301             // If the app isn't explicitly stopped, then always show the
1302             // force stop button.
1303             updateForceStopButton(true);
1304         } else {
1305             Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
1306                     Uri.fromParts("package", mAppEntry.info.packageName, null));
1307             intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
1308             intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
1309             intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
1310             getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
1311                     mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
1312         }
1313     }
1314
1315     static class DisableChanger extends AsyncTask<Object, Object, Object> {
1316         final PackageManager mPm;
1317         final WeakReference<InstalledAppDetails> mActivity;
1318         final ApplicationInfo mInfo;
1319         final int mState;
1320
1321         DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
1322             mPm = activity.mPm;
1323             mActivity = new WeakReference<InstalledAppDetails>(activity);
1324             mInfo = info;
1325             mState = state;
1326         }
1327
1328         @Override
1329         protected Object doInBackground(Object... params) {
1330             mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
1331             return null;
1332         }
1333     }
1334
1335     private void setNotificationsEnabled(boolean enabled) {
1336         String packageName = mAppEntry.info.packageName;
1337         INotificationManager nm = INotificationManager.Stub.asInterface(
1338                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
1339         try {
1340             final boolean enable = mNotificationSwitch.isChecked();
1341             nm.setNotificationsEnabledForPackage(packageName, mAppEntry.info.uid, enabled);
1342         } catch (android.os.RemoteException ex) {
1343             mNotificationSwitch.setChecked(!enabled); // revert
1344         }
1345     }
1346
1347     private int getPremiumSmsPermission(String packageName) {
1348         try {
1349             if (mSmsManager != null) {
1350                 return mSmsManager.getPremiumSmsPermission(packageName);
1351             }
1352         } catch (RemoteException ex) {
1353             // ignored
1354         }
1355         return SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN;
1356     }
1357
1358     /*
1359      * Method implementing functionality of buttons clicked
1360      * @see android.view.View.OnClickListener#onClick(android.view.View)
1361      */
1362     public void onClick(View v) {
1363         String packageName = mAppEntry.info.packageName;
1364         if(v == mUninstallButton) {
1365             if (mUpdatedSysApp) {
1366                 showDialogInner(DLG_FACTORY_RESET, 0);
1367             } else {
1368                 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
1369                     if (mAppEntry.info.enabled) {
1370                         showDialogInner(DLG_DISABLE, 0);
1371                     } else {
1372                         new DisableChanger(this, mAppEntry.info,
1373                                 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
1374                         .execute((Object)null);
1375                     }
1376                 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
1377                     uninstallPkg(packageName, true, false);
1378                 } else {
1379                     uninstallPkg(packageName, false, false);
1380                 }
1381             }
1382         } else if(v == mSpecialDisableButton) {
1383             showDialogInner(DLG_SPECIAL_DISABLE, 0);
1384         } else if(v == mActivitiesButton) {
1385             if (mUsbManager != null) {
1386                 mPm.clearPackagePreferredActivities(packageName);
1387                 try {
1388                     mUsbManager.clearDefaults(packageName, UserHandle.myUserId());
1389                 } catch (RemoteException e) {
1390                     Log.e(TAG, "mUsbManager.clearDefaults", e);
1391                 }
1392                 mAppWidgetManager.setBindAppWidgetPermission(packageName, false);
1393                 TextView autoLaunchTitleView =
1394                         (TextView) mRootView.findViewById(R.id.auto_launch_title);
1395                 TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
1396                 resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
1397             }
1398         } else if(v == mClearDataButton) {
1399             if (mAppEntry.info.manageSpaceActivityName != null) {
1400                 if (!Utils.isMonkeyRunning()) {
1401                     Intent intent = new Intent(Intent.ACTION_DEFAULT);
1402                     intent.setClassName(mAppEntry.info.packageName,
1403                             mAppEntry.info.manageSpaceActivityName);
1404                     startActivityForResult(intent, REQUEST_MANAGE_SPACE);
1405                 }
1406             } else {
1407                 showDialogInner(DLG_CLEAR_DATA, 0);
1408             }
1409         } else if (v == mClearCacheButton) {
1410             // Lazy initialization of observer
1411             if (mClearCacheObserver == null) {
1412                 mClearCacheObserver = new ClearCacheObserver();
1413             }
1414             mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
1415         } else if (v == mForceStopButton) {
1416             showDialogInner(DLG_FORCE_STOP, 0);
1417             //forceStopPackage(mAppInfo.packageName);
1418         } else if (v == mMoveAppButton) {
1419             if (mPackageMoveObserver == null) {
1420                 mPackageMoveObserver = new PackageMoveObserver();
1421             }
1422             int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
1423                     PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA;
1424             mMoveInProgress = true;
1425             refreshButtons();
1426             mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags);
1427         }
1428     }
1429
1430     @Override
1431     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1432         String packageName = mAppEntry.info.packageName;
1433         ActivityManager am = (ActivityManager)
1434                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
1435         if (buttonView == mAskCompatibilityCB) {
1436             am.setPackageAskScreenCompat(packageName, isChecked);
1437         } else if (buttonView == mEnableCompatibilityCB) {
1438             am.setPackageScreenCompatMode(packageName, isChecked ?
1439                     ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED);
1440         } else if (buttonView == mNotificationSwitch) {
1441             if (!isChecked) {
1442                 showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0);
1443             } else {
1444                 setNotificationsEnabled(true);
1445             }
1446         }
1447     }
1448 }
1449