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