UninstallerActivity.java revision 72d0d86c65a6e02e17332cf9a55675e893087645
1/* 2** 3** Copyright 2007, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17package com.android.packageinstaller; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.Dialog; 22import android.app.DialogFragment; 23import android.app.Fragment; 24import android.app.FragmentTransaction; 25import android.content.ComponentName; 26import android.content.DialogInterface; 27import android.content.Intent; 28import android.content.pm.ActivityInfo; 29import android.content.pm.ApplicationInfo; 30import android.content.pm.IPackageDeleteObserver2; 31import android.content.pm.IPackageManager; 32import android.content.pm.PackageInstaller; 33import android.content.pm.PackageManager; 34import android.content.pm.UserInfo; 35import android.net.Uri; 36import android.os.Bundle; 37import android.os.IBinder; 38import android.os.RemoteException; 39import android.os.ServiceManager; 40import android.os.UserHandle; 41import android.os.UserManager; 42import android.util.Log; 43 44/* 45 * This activity presents UI to uninstall an application. Usually launched with intent 46 * Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute 47 * com.android.packageinstaller.PackageName set to the application package name 48 */ 49public class UninstallerActivity extends Activity { 50 private static final String TAG = "UninstallerActivity"; 51 52 public static class UninstallAlertDialogFragment extends DialogFragment implements 53 DialogInterface.OnClickListener { 54 55 @Override 56 public Dialog onCreateDialog(Bundle savedInstanceState) { 57 final PackageManager pm = getActivity().getPackageManager(); 58 final DialogInfo dialogInfo = ((UninstallerActivity) getActivity()).mDialogInfo; 59 final CharSequence appLabel = dialogInfo.appInfo.loadLabel(pm); 60 61 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); 62 StringBuilder messageBuilder = new StringBuilder(); 63 64 // If the Activity label differs from the App label, then make sure the user 65 // knows the Activity belongs to the App being uninstalled. 66 if (dialogInfo.activityInfo != null) { 67 final CharSequence activityLabel = dialogInfo.activityInfo.loadLabel(pm); 68 if (!activityLabel.equals(appLabel)) { 69 messageBuilder.append( 70 getString(R.string.uninstall_activity_text, activityLabel)); 71 messageBuilder.append(" ").append(appLabel).append(".\n\n"); 72 } 73 } 74 75 final boolean isUpdate = 76 ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); 77 if (isUpdate) { 78 messageBuilder.append(getString(R.string.uninstall_update_text)); 79 } else { 80 UserManager userManager = UserManager.get(getActivity()); 81 if (dialogInfo.allUsers && userManager.getUserCount() >= 2) { 82 messageBuilder.append(getString(R.string.uninstall_application_text_all_users)); 83 } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) { 84 UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier()); 85 messageBuilder.append( 86 getString(R.string.uninstall_application_text_user, userInfo.name)); 87 } else { 88 messageBuilder.append(getString(R.string.uninstall_application_text)); 89 } 90 } 91 92 dialogBuilder.setTitle(appLabel); 93 dialogBuilder.setIcon(dialogInfo.appInfo.loadIcon(pm)); 94 dialogBuilder.setPositiveButton(android.R.string.ok, this); 95 dialogBuilder.setNegativeButton(android.R.string.cancel, this); 96 dialogBuilder.setMessage(messageBuilder.toString()); 97 return dialogBuilder.create(); 98 } 99 100 @Override 101 public void onClick(DialogInterface dialog, int which) { 102 if (which == Dialog.BUTTON_POSITIVE) { 103 ((UninstallerActivity) getActivity()).startUninstallProgress(); 104 } else { 105 ((UninstallerActivity) getActivity()).dispatchAborted(); 106 } 107 } 108 109 @Override 110 public void onDismiss(DialogInterface dialog) { 111 super.onDismiss(dialog); 112 getActivity().finish(); 113 } 114 } 115 116 public static class AppNotFoundDialogFragment extends DialogFragment { 117 118 @Override 119 public Dialog onCreateDialog(Bundle savedInstanceState) { 120 return new AlertDialog.Builder(getActivity()) 121 .setTitle(R.string.app_not_found_dlg_title) 122 .setMessage(R.string.app_not_found_dlg_text) 123 .setNeutralButton(android.R.string.ok, null) 124 .create(); 125 } 126 127 @Override 128 public void onDismiss(DialogInterface dialog) { 129 super.onDismiss(dialog); 130 ((UninstallerActivity) getActivity()).dispatchAborted(); 131 getActivity().setResult(Activity.RESULT_FIRST_USER); 132 getActivity().finish(); 133 } 134 } 135 136 static class DialogInfo { 137 ApplicationInfo appInfo; 138 ActivityInfo activityInfo; 139 boolean allUsers; 140 UserHandle user; 141 IBinder callback; 142 } 143 144 private String mPackageName; 145 private DialogInfo mDialogInfo; 146 147 @Override 148 public void onCreate(Bundle icicle) { 149 super.onCreate(icicle); 150 // Get intent information. 151 // We expect an intent with URI of the form package://<packageName>#<className> 152 // className is optional; if specified, it is the activity the user chose to uninstall 153 final Intent intent = getIntent(); 154 final Uri packageUri = intent.getData(); 155 if (packageUri == null) { 156 Log.e(TAG, "No package URI in intent"); 157 showAppNotFound(); 158 return; 159 } 160 mPackageName = packageUri.getEncodedSchemeSpecificPart(); 161 if (mPackageName == null) { 162 Log.e(TAG, "Invalid package name in URI: " + packageUri); 163 showAppNotFound(); 164 return; 165 } 166 167 final IPackageManager pm = IPackageManager.Stub.asInterface( 168 ServiceManager.getService("package")); 169 170 mDialogInfo = new DialogInfo(); 171 172 mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER); 173 if (mDialogInfo.user == null) { 174 mDialogInfo.user = android.os.Process.myUserHandle(); 175 } 176 177 mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false); 178 mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK); 179 180 try { 181 mDialogInfo.appInfo = pm.getApplicationInfo(mPackageName, 182 PackageManager.GET_UNINSTALLED_PACKAGES, mDialogInfo.user.getIdentifier()); 183 } catch (RemoteException e) { 184 Log.e(TAG, "Unable to get packageName. Package manager is dead?"); 185 } 186 187 if (mDialogInfo.appInfo == null) { 188 Log.e(TAG, "Invalid packageName: " + mPackageName); 189 showAppNotFound(); 190 return; 191 } 192 193 // The class name may have been specified (e.g. when deleting an app from all apps) 194 final String className = packageUri.getFragment(); 195 if (className != null) { 196 try { 197 mDialogInfo.activityInfo = pm.getActivityInfo( 198 new ComponentName(mPackageName, className), 0, 199 mDialogInfo.user.getIdentifier()); 200 } catch (RemoteException e) { 201 Log.e(TAG, "Unable to get className. Package manager is dead?"); 202 // Continue as the ActivityInfo isn't critical. 203 } 204 } 205 206 showConfirmationDialog(); 207 } 208 209 private void showConfirmationDialog() { 210 showDialogFragment(new UninstallAlertDialogFragment()); 211 } 212 213 private void showAppNotFound() { 214 showDialogFragment(new AppNotFoundDialogFragment()); 215 } 216 217 private void showDialogFragment(DialogFragment fragment) { 218 FragmentTransaction ft = getFragmentManager().beginTransaction(); 219 Fragment prev = getFragmentManager().findFragmentByTag("dialog"); 220 if (prev != null) { 221 ft.remove(prev); 222 } 223 fragment.show(ft, "dialog"); 224 } 225 226 void startUninstallProgress() { 227 Intent newIntent = new Intent(Intent.ACTION_VIEW); 228 newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user); 229 newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers); 230 newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback); 231 newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo); 232 if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { 233 newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); 234 newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 235 } 236 newIntent.setClass(this, UninstallAppProgress.class); 237 startActivity(newIntent); 238 } 239 240 void dispatchAborted() { 241 if (mDialogInfo != null && mDialogInfo.callback != null) { 242 final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface( 243 mDialogInfo.callback); 244 try { 245 observer.onPackageDeleted(mPackageName, 246 PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user"); 247 } catch (RemoteException ignored) { 248 } 249 } 250 } 251} 252