blob: 712f94db0e9fea152f0bfffe04fec34260568ef5 [file] [log] [blame]
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001
2
3/**
4 * Copyright (C) 2007 The Android Open Source Project
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
7 * use this file except in compliance with the License. You may obtain a copy
8 * of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15 * License for the specific language governing permissions and limitations
16 * under the License.
17 */
18
19package com.android.settings;
20
21import com.android.settings.R;
22import android.app.Activity;
23import android.app.ActivityManager;
24import android.app.AlertDialog;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.ApplicationInfo;
30import android.content.pm.IPackageDataObserver;
31import android.content.pm.IPackageStatsObserver;
32import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.PackageStats;
35import android.content.pm.PackageManager.NameNotFoundException;
36import android.net.Uri;
37import android.os.Bundle;
38import android.os.Handler;
39import android.os.Message;
40import android.util.Config;
41import android.util.Log;
42import java.util.ArrayList;
43import java.util.List;
44import android.content.ComponentName;
45import android.view.View;
46import android.widget.AppSecurityPermissions;
47import android.widget.Button;
48import android.widget.ImageView;
49import android.widget.LinearLayout;
50import android.widget.TextView;
51
52/**
53 * Activity to display application information from Settings
54 *
55 */
56public class InstalledAppDetails extends Activity implements View.OnClickListener, DialogInterface.OnClickListener {
57 private static final String TAG="InstalledAppDetails";
58 private static final int _UNKNOWN_APP=R.string.unknown;
59 //wait times used for the async package manager api
60 private ApplicationInfo mAppInfo;
61 private Button mUninstallButton;
62 private Button mActivitiesButton;
63 private boolean mSysPackage;
64 private boolean localLOGV=Config.LOGV || true;
65 private TextView mTotalSize;
66 private TextView mAppSize;
67 private TextView mDataSize;
68 PackageStats mSizeInfo;
69 private Button mManageSpaceButton;
70 private PackageManager mPm;
71 private String mBStr, mKbStr, mMbStr;
72
73 //internal constants used in Handler
74 private static final int CLEAR_USER_DATA = 1;
75 private static final int OP_SUCCESSFUL = 1;
76 private static final int OP_FAILED = 2;
77 private static final int GET_PKG_SIZE = 2;
78 private static final String ATTR_PACKAGE_STATS="PackageStats";
79
80 private Handler mHandler = new Handler() {
81 public void handleMessage(Message msg) {
82 switch (msg.what) {
83 case CLEAR_USER_DATA:
84 processClearMsg(msg);
85 break;
86 case GET_PKG_SIZE:
87 refreshSizeInfo(msg);
88 break;
89 default:
90 break;
91 }
92 }
93 };
94
95 private boolean isSystemPackage() {
96 if ((mAppInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
97 return true;
98 }
99 return false;
100 }
101
102 class ClearUserDataObserver extends IPackageDataObserver.Stub {
103 public void onRemoveCompleted(final String packageName, final boolean succeeded) {
104 final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
105 msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
106 mHandler.sendMessage(msg);
107 }
108 }
109
110 class PkgSizeObserver extends IPackageStatsObserver.Stub {
111 public int idx;
112 public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) {
113 Message msg = mHandler.obtainMessage(GET_PKG_SIZE);
114 Bundle data = new Bundle();
115 data.putParcelable(ATTR_PACKAGE_STATS, pStats);
116 msg.setData(data);
117 mHandler.sendMessage(msg);
118
119 }
120 }
121
122 private String getSizeStr(long size) {
123 String retStr = "";
124 if(size < 1024) {
125 return String.valueOf(size)+mBStr;
126 }
127 long kb, mb, rem;
128 kb = size >> 10;
129 rem = size - (kb << 10);
130 if(kb < 1024) {
131 if(rem > 512) {
132 kb++;
133 }
134 retStr += String.valueOf(kb)+mKbStr;
135 return retStr;
136 }
137 mb = kb >> 10;
138 if(kb >= 512) {
139 //round off
140 mb++;
141 }
142 retStr += String.valueOf(mb)+ mMbStr;
143 return retStr;
144 }
145
146 /** Called when the activity is first created. */
147 @Override
148 protected void onCreate(Bundle icicle) {
149 super.onCreate(icicle);
150 //get package manager
151 mPm = getPackageManager();
152 //get application's name from intent
153 Intent intent = getIntent();
154 final String packageName = intent.getStringExtra(ManageApplications.APP_PKG_NAME);
155 mSizeInfo = intent.getParcelableExtra(ManageApplications.APP_PKG_SIZE);
156 long total = -1;
157 long code = -1;
158 long data = -1;
159 if(mSizeInfo != null) {
160 total = mSizeInfo.cacheSize+mSizeInfo.codeSize+mSizeInfo.dataSize;
161 code = mSizeInfo.codeSize;
162 data = mSizeInfo.dataSize+mSizeInfo.cacheSize;
163 }
164 String unknownStr = getString(_UNKNOWN_APP);
165 mBStr = getString(R.string.b_text);
166 mKbStr = getString(R.string.kb_text);
167 mMbStr = getString(R.string.mb_text);
168 String totalSizeStr = unknownStr;
169 if(total != -1) {
170 totalSizeStr = getSizeStr(total);
171 }
172 String appSizeStr = unknownStr;
173 if(code != -1) {
174 appSizeStr = getSizeStr(code);
175 }
176 String dataSizeStr = unknownStr;
177 if(data != -1) {
178 dataSizeStr = getSizeStr(data);
179 }
180 if(localLOGV) Log.i(TAG, "packageName:"+packageName+", total="+total+
181 "code="+code+", data="+data);
182 try {
183 mAppInfo = mPm.getApplicationInfo(packageName, 0);
184 } catch (NameNotFoundException e) {
185 Throwable th = e.fillInStackTrace();
186 Log.e(TAG, "Exception when retrieving package:"+packageName, e);
187 displayErrorDialog(R.string.app_not_found_dlg_text, true, true);
188 }
189 setContentView(R.layout.installed_app_details);
190 ((ImageView)findViewById(R.id.app_icon)).setImageDrawable(mPm.
191 getApplicationIcon(mAppInfo));
192 //set application name TODO version
193 CharSequence appName = mPm.getApplicationLabel(mAppInfo);
194 if(appName == null) {
195 appName = getString(_UNKNOWN_APP);
196 }
197 ((TextView)findViewById(R.id.app_name)).setText(appName);
198 CharSequence appDesc = mAppInfo.loadDescription(mPm);
199 if(appDesc != null) {
200 ((TextView)findViewById(R.id.app_description)).setText(appDesc);
201 }
202 //TODO download str and download url
203 //set values on views
204 mTotalSize = (TextView)findViewById(R.id.total_size_text);
205 mTotalSize.setText(totalSizeStr);
206 mAppSize = (TextView)findViewById(R.id.application_size_text);
207 mAppSize.setText(appSizeStr);
208 mDataSize = (TextView)findViewById(R.id.data_size_text);
209 mDataSize.setText(dataSizeStr);
210
211 mUninstallButton = ((Button)findViewById(R.id.uninstall_button));
212 //determine if app is a system app
213 mSysPackage = isSystemPackage();
214 if(localLOGV) Log.i(TAG, "Is systemPackage "+mSysPackage);
215 int btnText;
216 boolean btnClickable = true;
217
218 if(mSysPackage) {
219 //app can clear user data
220 if((mAppInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)
221 == ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) {
222 mUninstallButton.setText(R.string.clear_user_data_text);
223 //disable button if data is 0
224 if(data == 0) {
225 mUninstallButton.setEnabled(false);
226 } else {
227 //enable button
228 mUninstallButton.setOnClickListener(this);
229 }
230 } else {
231 //hide button if diableClearUserData is set
232 mUninstallButton.setVisibility(View.GONE);
233 }
234 } else {
235 mUninstallButton.setText(R.string.uninstall_text);
236 mUninstallButton.setOnClickListener(this);
237 }
238 //clear activities
239 mActivitiesButton = (Button)findViewById(R.id.clear_activities_button);
240 List<ComponentName> prefActList = new ArrayList<ComponentName>();
241 //intent list cannot be null. so pass empty list
242 List<IntentFilter> intentList = new ArrayList<IntentFilter>();
243 mPm.getPreferredActivities(intentList, prefActList, packageName);
244 if(localLOGV) Log.i(TAG, "Have "+prefActList.size()+" number of activities in prefered list");
245 TextView autoLaunchView = (TextView)findViewById(R.id.auto_launch);
246 if(prefActList.size() <= 0) {
247 //disable clear activities button
248 autoLaunchView.setText(R.string.auto_launch_disable_text);
249 mActivitiesButton.setEnabled(false);
250 } else {
251 autoLaunchView.setText(R.string.auto_launch_enable_text);
252 mActivitiesButton.setOnClickListener(this);
253 }
254 mManageSpaceButton = (Button)findViewById(R.id.manage_space_button);
255 if(mAppInfo.manageSpaceActivityName != null) {
256 mManageSpaceButton.setVisibility(View.VISIBLE);
257 mManageSpaceButton.setOnClickListener(this);
258 }
259 //security permissions section
260 AppSecurityPermissions asp = new AppSecurityPermissions(this);
261 PackageInfo pkgInfo;
262 try {
263 pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
264 } catch (NameNotFoundException e) {
265 Log.w(TAG, "Couldnt retrieve permissions for package:"+packageName);
266 return;
267 }
268 asp.setSecurityPermissionsView(pkgInfo);
269 LinearLayout securityList = (LinearLayout) findViewById(R.id.security_settings_list);
270 securityList.addView(asp.getPermissionsView());
271 }
272
273 private void displayErrorDialog(int msgId, final boolean finish, final boolean changed) {
274 //display confirmation dialog
275 new AlertDialog.Builder(this)
276 .setTitle(getString(R.string.app_not_found_dlg_title))
277 .setIcon(R.drawable.ic_dialog_alert)
278 .setMessage(getString(msgId))
279 .setNeutralButton(getString(R.string.dlg_ok),
280 new DialogInterface.OnClickListener() {
281 public void onClick(DialogInterface dialog, int which) {
282 //force to recompute changed value
283 setIntentAndFinish(finish, changed);
284 }
285 }
286 )
287 .show();
288 }
289
290 private void setIntentAndFinish(boolean finish, boolean appChanged) {
291 if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
292 Intent intent = new Intent();
293 intent.putExtra(ManageApplications.APP_CHG, appChanged);
294 setResult(ManageApplications.RESULT_OK, intent);
295 mUninstallButton.setEnabled(false);
296 if(finish) {
297 finish();
298 }
299 }
300
301 /*
302 * Private method to handle get size info notification from observer when
303 * the async operation from PackageManager is complete. The current user data
304 * info has to be refreshed in the manage applications screen as well as the current screen.
305 */
306 private void refreshSizeInfo(Message msg) {
307 boolean changed = false;
308 Intent intent = new Intent();
309 PackageStats newPs = msg.getData().getParcelable(ATTR_PACKAGE_STATS);
310 long newTot = newPs.cacheSize+newPs.codeSize+newPs.dataSize;
311 long oldTot = mSizeInfo.cacheSize+mSizeInfo.codeSize+mSizeInfo.dataSize;
312 if(newTot != oldTot) {
313 mTotalSize.setText(getSizeStr(newTot));
314 changed = true;
315 }
316 if(newPs.codeSize != mSizeInfo.codeSize) {
317 mAppSize.setText(getSizeStr(newPs.codeSize));
318 changed = true;
319 }
320 if((newPs.dataSize != mSizeInfo.dataSize) || (newPs.cacheSize != mSizeInfo.cacheSize)) {
321 mDataSize.setText(getSizeStr(newPs.dataSize+newPs.cacheSize));
322 changed = true;
323 }
324 if(changed) {
325 mUninstallButton.setText(R.string.clear_user_data_text);
326 mSizeInfo = newPs;
327 intent.putExtra(ManageApplications.APP_PKG_SIZE, mSizeInfo);
328 }
329 intent.putExtra(ManageApplications.APP_CHG, changed);
330 setResult(ManageApplications.RESULT_OK, intent);
331 }
332
333 /*
334 * Private method to handle clear message notification from observer when
335 * the async operation from PackageManager is complete
336 */
337 private void processClearMsg(Message msg) {
338 int result = msg.arg1;
339 String packageName = mAppInfo.packageName;
340 if(result == OP_SUCCESSFUL) {
341 Log.i(TAG, "Cleared user data for system package:"+packageName);
342 PkgSizeObserver observer = new PkgSizeObserver();
343 mPm.getPackageSizeInfo(packageName, observer);
344 } else {
345 mUninstallButton.setText(R.string.clear_user_data_text);
346 mUninstallButton.setEnabled(true);
347 }
348 }
349
350 /*
351 * Private method to initiate clearing user data when the user clicks the clear data
352 * button for a system package
353 */
354 private void initiateClearUserDataForSysPkg() {
355 mUninstallButton.setEnabled(false);
356 //invoke uninstall or clear user data based on sysPackage
357 boolean recomputeSizes = false;
358 String packageName = mAppInfo.packageName;
359 Log.i(TAG, "Clearing user data for system package");
360 ClearUserDataObserver observer = new ClearUserDataObserver();
361 ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
362 boolean res = am.clearApplicationUserData(packageName, observer);
363 if(!res) {
364 //doesnt initiate clear. some error. should not happen but just log error for now
365 Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
366 displayErrorDialog(R.string.clear_data_failed, false, false);
367 } else {
368 mUninstallButton.setText(R.string.recompute_size);
369 }
370 }
371
372 /*
373 * Method implementing functionality of buttons clicked
374 * @see android.view.View.OnClickListener#onClick(android.view.View)
375 */
376 public void onClick(View v) {
377 String packageName = mAppInfo.packageName;
378 if(v == mUninstallButton) {
379 if(mSysPackage) {
380 //display confirmation dialog
381 new AlertDialog.Builder(this)
382 .setTitle(getString(R.string.clear_data_dlg_title))
383 .setIcon(R.drawable.ic_dialog_alert)
384 .setMessage(getString(R.string.clear_data_dlg_text))
385 .setPositiveButton(R.string.dlg_ok, this)
386 .setNegativeButton(R.string.dlg_cancel, this)
387 .show();
388 } else {
389 //create new intent to launch Uninstaller activity
390 Uri packageURI = Uri.parse("package:"+packageName);
391 Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
392 startActivity(uninstallIntent);
393 setIntentAndFinish(true, false);
394 }
395 } else if(v == mActivitiesButton) {
396 mPm.clearPackagePreferredActivities(packageName);
397 mActivitiesButton.setEnabled(false);
398 } else if(v == mManageSpaceButton) {
399 Intent intent = new Intent(Intent.ACTION_DEFAULT);
400 intent.setClassName(mAppInfo.packageName, mAppInfo.manageSpaceActivityName);
401 startActivityForResult(intent, -1);
402 }
403 }
404
405 public void onClick(DialogInterface dialog, int which) {
406 if(which == AlertDialog.BUTTON1) {
407 //invoke uninstall or clear user data based on sysPackage
408 initiateClearUserDataForSysPkg();
409 } else {
410 //cancel do nothing just retain existing screen
411 }
412 }
413}
414