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.content.ComponentName;
23import android.content.Context;
24import android.content.DialogInterface;
25import android.content.Intent;
26import android.content.pm.ActivityInfo;
27import android.content.pm.ApplicationInfo;
28import android.content.pm.PackageManager;
29import android.content.pm.PackageManager.NameNotFoundException;
30import android.content.pm.ResolveInfo;
31import android.graphics.drawable.Drawable;
32import android.net.Uri;
33import android.os.Bundle;
34import android.os.UserManager;
35import android.util.Log;
36import android.view.LayoutInflater;
37import android.view.View;
38import android.view.View.OnClickListener;
39import android.view.ViewGroup;
40import android.view.Window;
41import android.widget.Button;
42import android.widget.TextView;
43
44import java.util.List;
45
46/*
47 * This activity presents UI to uninstall an application. Usually launched with intent
48 * Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute
49 * com.android.packageinstaller.PackageName set to the application package name
50 */
51public class UninstallerActivity extends Activity implements OnClickListener,
52        DialogInterface.OnCancelListener {
53    private static final String TAG = "UninstallerActivity";
54    private boolean localLOGV = false;
55    PackageManager mPm;
56    private ApplicationInfo mAppInfo;
57    private boolean mAllUsers;
58    private Button mOk;
59    private Button mCancel;
60
61    // Dialog identifiers used in showDialog
62    private static final int DLG_BASE = 0;
63    private static final int DLG_APP_NOT_FOUND = DLG_BASE + 1;
64    private static final int DLG_UNINSTALL_FAILED = DLG_BASE + 2;
65
66    @Override
67    public Dialog onCreateDialog(int id) {
68        switch (id) {
69        case DLG_APP_NOT_FOUND :
70            return new AlertDialog.Builder(this)
71                    .setTitle(R.string.app_not_found_dlg_title)
72                    .setIcon(com.android.internal.R.drawable.ic_dialog_alert)
73                    .setMessage(R.string.app_not_found_dlg_text)
74                    .setNeutralButton(getString(R.string.dlg_ok),
75                            new DialogInterface.OnClickListener() {
76                                public void onClick(DialogInterface dialog, int which) {
77                                    setResult(Activity.RESULT_FIRST_USER);
78                                    finish();
79                                }})
80                    .create();
81        case DLG_UNINSTALL_FAILED :
82            // Guaranteed not to be null. will default to package name if not set by app
83           CharSequence appTitle = mPm.getApplicationLabel(mAppInfo);
84           String dlgText = getString(R.string.uninstall_failed_msg,
85                    appTitle.toString());
86            // Display uninstall failed dialog
87            return new AlertDialog.Builder(this)
88                    .setTitle(R.string.uninstall_failed)
89                    .setIcon(com.android.internal.R.drawable.ic_dialog_alert)
90                    .setMessage(dlgText)
91                    .setNeutralButton(getString(R.string.dlg_ok),
92                            new DialogInterface.OnClickListener() {
93                                public void onClick(DialogInterface dialog, int which) {
94                                    setResult(Activity.RESULT_FIRST_USER);
95                                    finish();
96                                }})
97                    .create();
98        }
99        return null;
100    }
101
102    private void startUninstallProgress() {
103        Intent newIntent = new Intent(Intent.ACTION_VIEW);
104        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
105                                                  mAppInfo);
106        newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mAllUsers);
107        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
108            newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
109            newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
110        }
111        newIntent.setClass(this, UninstallAppProgress.class);
112        startActivity(newIntent);
113        finish();
114    }
115
116    @Override
117    public void onCreate(Bundle icicle) {
118        super.onCreate(icicle);
119        // Get intent information.
120        // We expect an intent with URI of the form package://<packageName>#<className>
121        // className is optional; if specified, it is the activity the user chose to uninstall
122        final Intent intent = getIntent();
123        Uri packageURI = intent.getData();
124        String packageName = packageURI.getEncodedSchemeSpecificPart();
125        if(packageName == null) {
126            Log.e(TAG, "Invalid package name:" + packageName);
127            showDialog(DLG_APP_NOT_FOUND);
128            return;
129        }
130
131        mPm = getPackageManager();
132        boolean errFlag = false;
133        try {
134            mAppInfo = mPm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
135        } catch (NameNotFoundException e) {
136            errFlag = true;
137        }
138
139        mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
140
141        // The class name may have been specified (e.g. when deleting an app from all apps)
142        String className = packageURI.getFragment();
143        ActivityInfo activityInfo = null;
144        if (className != null) {
145            try {
146                activityInfo = mPm.getActivityInfo(new ComponentName(packageName, className), 0);
147            } catch (NameNotFoundException e) {
148                errFlag = true;
149            }
150        }
151
152        if(mAppInfo == null || errFlag) {
153            Log.e(TAG, "Invalid packageName or componentName in " + packageURI.toString());
154            showDialog(DLG_APP_NOT_FOUND);
155        } else {
156            boolean isUpdate = ((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
157
158            setContentView(R.layout.uninstall_confirm);
159
160            TextView confirm = (TextView) findViewById(R.id.uninstall_confirm);
161            if (isUpdate) {
162                setTitle(R.string.uninstall_update_title);
163                confirm.setText(R.string.uninstall_update_text);
164            } else {
165                setTitle(R.string.uninstall_application_title);
166                if (mAllUsers && ((UserManager)getSystemService(
167                        Context.USER_SERVICE)).getUsers().size() >= 2) {
168                    confirm.setText(R.string.uninstall_application_text_all_users);
169                } else {
170                    confirm.setText(R.string.uninstall_application_text);
171                }
172            }
173
174            // If an activity was specified (e.g. when dragging from All Apps to trash can),
175            // give a bit more info if the activity label isn't the same as the package label.
176            if (activityInfo != null) {
177                CharSequence activityLabel = activityInfo.loadLabel(mPm);
178                if (!activityLabel.equals(mAppInfo.loadLabel(mPm))) {
179                    TextView activityText = (TextView) findViewById(R.id.activity_text);
180                    CharSequence text = getString(R.string.uninstall_activity_text, activityLabel);
181                    activityText.setText(text);
182                    activityText.setVisibility(View.VISIBLE);
183                }
184            }
185
186            View snippetView = findViewById(R.id.uninstall_activity_snippet);
187            PackageUtil.initSnippetForInstalledApp(this, mAppInfo, snippetView);
188
189            //initialize ui elements
190            mOk = (Button)findViewById(R.id.ok_button);
191            mCancel = (Button)findViewById(R.id.cancel_button);
192            mOk.setOnClickListener(this);
193            mCancel.setOnClickListener(this);
194        }
195    }
196
197    public void onClick(View v) {
198        if(v == mOk) {
199            //initiate next screen
200            startUninstallProgress();
201        } else if(v == mCancel) {
202            finish();
203        }
204    }
205
206    public void onCancel(DialogInterface dialog) {
207        finish();
208    }
209}
210