2 * Copyright (C) 2008 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings.deviceinfo;
19 import com.android.settings.R;
20 import com.android.settings.SettingsPreferenceFragment;
21 import com.android.settings.deviceinfo.MemoryMeasurement.MeasurementReceiver;
23 import android.app.ActivityManager;
24 import android.app.AlertDialog;
25 import android.app.Dialog;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.DialogInterface.OnCancelListener;
32 import android.content.pm.ApplicationInfo;
33 import android.content.res.Resources;
34 import android.graphics.drawable.ShapeDrawable;
35 import android.graphics.drawable.shapes.RectShape;
36 import android.graphics.drawable.shapes.RoundRectShape;
37 import android.hardware.UsbManager;
38 import android.os.Bundle;
39 import android.os.Environment;
40 import android.os.Handler;
41 import android.os.IBinder;
42 import android.os.Message;
43 import android.os.RemoteException;
44 import android.os.ServiceManager;
45 import android.os.storage.IMountService;
46 import android.os.storage.StorageEventListener;
47 import android.os.storage.StorageManager;
48 import android.preference.Preference;
49 import android.preference.PreferenceGroup;
50 import android.preference.PreferenceScreen;
51 import android.text.format.Formatter;
52 import android.util.Log;
53 import android.widget.Toast;
55 import java.util.List;
57 public class Memory extends SettingsPreferenceFragment implements OnCancelListener,
59 private static final String TAG = "Memory";
60 static final boolean localLOGV = false;
62 private static final String MEMORY_SD_SIZE = "memory_sd_size";
64 private static final String MEMORY_SD_AVAIL = "memory_sd_avail";
66 private static final String MEMORY_SD_MOUNT_TOGGLE = "memory_sd_mount_toggle";
68 private static final String MEMORY_SD_FORMAT = "memory_sd_format";
70 private static final String MEMORY_SD_GROUP = "memory_sd";
72 private static final String MEMORY_INTERNAL_SIZE = "memory_internal_size";
74 private static final String MEMORY_INTERNAL_AVAIL = "memory_internal_avail";
76 private static final String MEMORY_INTERNAL_APPS = "memory_internal_apps";
78 private static final String MEMORY_INTERNAL_MEDIA = "memory_internal_media";
80 private static final String MEMORY_INTERNAL_CHART = "memory_internal_chart";
82 private static final int DLG_CONFIRM_UNMOUNT = 1;
83 private static final int DLG_ERROR_UNMOUNT = 2;
85 private Resources mRes;
87 // External storage preferences
88 private Preference mSdSize;
89 private Preference mSdAvail;
90 private Preference mSdMountToggle;
91 private Preference mSdFormat;
92 private PreferenceGroup mSdMountPreferenceGroup;
94 // Internal storage preferences
95 private Preference mInternalSize;
96 private Preference mInternalAvail;
97 private Preference mInternalMediaUsage;
98 private Preference mInternalAppsUsage;
99 private UsageBarPreference mInternalUsageChart;
101 // Internal storage chart colors
102 private int mInternalMediaColor;
103 private int mInternalAppsColor;
104 private int mInternalUsedColor;
106 boolean mSdMountToggleAdded = true;
108 // Access using getMountService()
109 private IMountService mMountService = null;
111 private StorageManager mStorageManager = null;
113 // Updates the memory usage bar graph.
114 private static final int MSG_UI_UPDATE_INTERNAL_APPROXIMATE = 1;
116 // Updates the memory usage bar graph.
117 private static final int MSG_UI_UPDATE_INTERNAL_EXACT = 2;
119 // Updates the memory usage stats for external.
120 private static final int MSG_UI_UPDATE_EXTERNAL_APPROXIMATE = 3;
122 private Handler mUpdateHandler = new Handler() {
124 public void handleMessage(Message msg) {
126 case MSG_UI_UPDATE_INTERNAL_APPROXIMATE: {
127 Bundle bundle = msg.getData();
128 final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE);
129 final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE);
130 updateUiApproximate(totalSize, availSize);
133 case MSG_UI_UPDATE_INTERNAL_EXACT: {
134 Bundle bundle = msg.getData();
135 final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE);
136 final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE);
137 final long mediaUsed = bundle.getLong(MemoryMeasurement.MEDIA_USED);
138 final long appsUsed = bundle.getLong(MemoryMeasurement.APPS_USED);
139 updateUiExact(totalSize, availSize, mediaUsed, appsUsed);
142 case MSG_UI_UPDATE_EXTERNAL_APPROXIMATE: {
143 Bundle bundle = msg.getData();
144 final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE);
145 final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE);
146 updateExternalStorage(totalSize, availSize);
153 private MemoryMeasurement mMeasurement;
156 public void onCreate(Bundle icicle) {
157 super.onCreate(icicle);
159 if (mStorageManager == null) {
160 mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
161 mStorageManager.registerListener(mStorageListener);
164 addPreferencesFromResource(R.xml.device_info_memory);
166 mRes = getResources();
167 mSdSize = findPreference(MEMORY_SD_SIZE);
168 mSdAvail = findPreference(MEMORY_SD_AVAIL);
169 mSdMountToggle = findPreference(MEMORY_SD_MOUNT_TOGGLE);
170 mSdFormat = findPreference(MEMORY_SD_FORMAT);
171 mSdMountPreferenceGroup = (PreferenceGroup)findPreference(MEMORY_SD_GROUP);
173 if (Environment.isExternalStorageEmulated()) {
174 getPreferenceScreen().removePreference(mSdMountPreferenceGroup);
177 mInternalSize = findPreference(MEMORY_INTERNAL_SIZE);
178 mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL);
179 mInternalMediaUsage = findPreference(MEMORY_INTERNAL_MEDIA);
180 mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS);
182 mInternalMediaColor = mRes.getColor(R.color.memory_media_usage);
183 mInternalAppsColor = mRes.getColor(R.color.memory_apps_usage);
184 mInternalUsedColor = mRes.getColor(R.color.memory_used);
186 float[] radius = new float[] {
187 5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f
189 RoundRectShape shape1 = new RoundRectShape(radius, null, null);
191 ShapeDrawable mediaShape = new ShapeDrawable(shape1);
192 mediaShape.setIntrinsicWidth(32);
193 mediaShape.setIntrinsicHeight(32);
194 mediaShape.getPaint().setColor(mInternalMediaColor);
195 mInternalMediaUsage.setIcon(mediaShape);
197 ShapeDrawable appsShape = new ShapeDrawable(shape1);
198 appsShape.setIntrinsicWidth(32);
199 appsShape.setIntrinsicHeight(32);
200 appsShape.getPaint().setColor(mInternalAppsColor);
201 mInternalAppsUsage.setIcon(appsShape);
203 mInternalUsageChart = (UsageBarPreference) findPreference(MEMORY_INTERNAL_CHART);
205 mMeasurement = MemoryMeasurement.getInstance(getActivity());
206 mMeasurement.setReceiver(this);
210 public void onResume() {
213 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
214 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
215 intentFilter.addDataScheme("file");
216 getActivity().registerReceiver(mReceiver, intentFilter);
218 mMeasurement.invalidate();
219 if (!Environment.isExternalStorageEmulated()) {
220 mMeasurement.measureExternal();
222 mMeasurement.measureInternal();
225 StorageEventListener mStorageListener = new StorageEventListener() {
227 public void onStorageStateChanged(String path, String oldState, String newState) {
228 Log.i(TAG, "Received storage state changed notification that " +
229 path + " changed state from " + oldState +
231 if (!Environment.isExternalStorageEmulated()) {
232 mMeasurement.measureExternal();
238 public void onPause() {
240 getActivity().unregisterReceiver(mReceiver);
241 mMeasurement.cleanUp();
245 public void onDestroy() {
246 if (mStorageManager != null && mStorageListener != null) {
247 mStorageManager.unregisterListener(mStorageListener);
252 private synchronized IMountService getMountService() {
253 if (mMountService == null) {
254 IBinder service = ServiceManager.getService("mount");
255 if (service != null) {
256 mMountService = IMountService.Stub.asInterface(service);
258 Log.e(TAG, "Can't get mount service");
261 return mMountService;
265 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
266 if (preference == mSdMountToggle) {
267 String status = Environment.getExternalStorageState();
268 if (status.equals(Environment.MEDIA_MOUNTED)) {
274 } else if (preference == mSdFormat) {
275 Intent intent = new Intent(Intent.ACTION_VIEW);
276 intent.setClass(getActivity(), com.android.settings.MediaFormat.class);
277 startActivity(intent);
279 } else if (preference == mInternalAppsUsage) {
280 Intent intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
281 intent.setClass(getActivity(),
282 com.android.settings.Settings.ManageApplicationsActivity.class);
283 startActivity(intent);
290 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
292 public void onReceive(Context context, Intent intent) {
293 mMeasurement.invalidate();
295 if (!Environment.isExternalStorageEmulated()) {
296 mMeasurement.measureExternal();
298 mMeasurement.measureInternal();
303 public Dialog onCreateDialog(int id) {
305 case DLG_CONFIRM_UNMOUNT:
306 return new AlertDialog.Builder(getActivity())
307 .setTitle(R.string.dlg_confirm_unmount_title)
308 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
309 public void onClick(DialogInterface dialog, int which) {
312 .setNegativeButton(R.string.cancel, null)
313 .setMessage(R.string.dlg_confirm_unmount_text)
314 .setOnCancelListener(this)
316 case DLG_ERROR_UNMOUNT:
317 return new AlertDialog.Builder(getActivity())
318 .setTitle(R.string.dlg_error_unmount_title)
319 .setNeutralButton(R.string.dlg_ok, null)
320 .setMessage(R.string.dlg_error_unmount_text)
321 .setOnCancelListener(this)
327 private void doUnmount(boolean force) {
328 // Present a toast here
329 Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
330 IMountService mountService = getMountService();
331 String extStoragePath = Environment.getExternalStorageDirectory().toString();
333 mSdMountToggle.setEnabled(false);
334 mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title));
335 mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary));
336 mountService.unmountVolume(extStoragePath, force);
337 } catch (RemoteException e) {
338 // Informative dialog to user that
340 showDialogInner(DLG_ERROR_UNMOUNT);
344 private void showDialogInner(int id) {
349 private boolean hasAppsAccessingStorage() throws RemoteException {
350 String extStoragePath = Environment.getExternalStorageDirectory().toString();
351 IMountService mountService = getMountService();
352 int stUsers[] = mountService.getStorageUsers(extStoragePath);
353 if (stUsers != null && stUsers.length > 0) {
356 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
357 List<ApplicationInfo> list = am.getRunningExternalApplications();
358 if (list != null && list.size() > 0) {
364 private void unmount() {
365 // Check if external media is in use.
367 if (hasAppsAccessingStorage()) {
368 if (localLOGV) Log.i(TAG, "Do have storage users accessing media");
369 // Present dialog to user
370 showDialogInner(DLG_CONFIRM_UNMOUNT);
374 } catch (RemoteException e) {
375 // Very unlikely. But present an error dialog anyway
376 Log.e(TAG, "Is MountService running?");
377 showDialogInner(DLG_ERROR_UNMOUNT);
381 private void mount() {
382 IMountService mountService = getMountService();
384 if (mountService != null) {
385 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
387 Log.e(TAG, "Mount service is null, can't mount");
389 } catch (RemoteException ex) {
393 private void updateUiExact(long totalSize, long availSize, long mediaSize, long appsSize) {
394 mInternalSize.setSummary(formatSize(totalSize));
395 mInternalAvail.setSummary(formatSize(availSize));
396 mInternalMediaUsage.setSummary(formatSize(mediaSize));
397 mInternalAppsUsage.setSummary(formatSize(appsSize));
399 mInternalUsageChart.clear();
400 mInternalUsageChart.addEntry(mediaSize / (float) totalSize, mInternalMediaColor);
401 mInternalUsageChart.addEntry(appsSize / (float) totalSize, mInternalAppsColor);
403 final long usedSize = totalSize - availSize;
405 // There are other things that can take up storage, but we didn't
407 final long remaining = usedSize - (mediaSize + appsSize);
409 mInternalUsageChart.addEntry(remaining / (float) totalSize, mInternalUsedColor);
411 mInternalUsageChart.commit();
414 private void updateUiApproximate(long totalSize, long availSize) {
415 mInternalSize.setSummary(formatSize(totalSize));
416 mInternalAvail.setSummary(formatSize(availSize));
418 final long usedSize = totalSize - availSize;
420 mInternalUsageChart.clear();
421 mInternalUsageChart.addEntry(usedSize / (float) totalSize, mInternalUsedColor);
422 mInternalUsageChart.commit();
425 private void updateExternalStorage(long totalSize, long availSize) {
426 String status = Environment.getExternalStorageState();
427 String readOnly = "";
428 if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
429 status = Environment.MEDIA_MOUNTED;
430 readOnly = mRes.getString(R.string.read_only);
433 if (status.equals(Environment.MEDIA_MOUNTED)) {
434 if (!Environment.isExternalStorageRemovable()) {
435 // This device has built-in storage that is not removable.
436 // There is no reason for the user to unmount it.
437 if (mSdMountToggleAdded) {
438 mSdMountPreferenceGroup.removePreference(mSdMountToggle);
439 mSdMountToggleAdded = false;
443 mSdSize.setSummary(formatSize(totalSize));
444 mSdAvail.setSummary(formatSize(availSize) + readOnly);
446 mSdMountToggle.setEnabled(true);
447 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
448 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
450 } catch (IllegalArgumentException e) {
451 // this can occur if the SD card is removed, but we haven't
453 // ACTION_MEDIA_REMOVED Intent yet.
454 status = Environment.MEDIA_REMOVED;
457 mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
458 mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
460 if (!Environment.isExternalStorageRemovable()) {
461 if (status.equals(Environment.MEDIA_UNMOUNTED)) {
462 if (!mSdMountToggleAdded) {
463 mSdMountPreferenceGroup.addPreference(mSdMountToggle);
464 mSdMountToggleAdded = true;
469 if (status.equals(Environment.MEDIA_UNMOUNTED) || status.equals(Environment.MEDIA_NOFS)
470 || status.equals(Environment.MEDIA_UNMOUNTABLE)) {
471 mSdMountToggle.setEnabled(true);
472 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
473 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
475 mSdMountToggle.setEnabled(false);
476 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
477 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
482 private String formatSize(long size) {
483 return Formatter.formatFileSize(getActivity(), size);
486 public void onCancel(DialogInterface dialog) {
487 // TODO: Is this really required?
492 public void updateApproximateExternal(Bundle bundle) {
493 final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXTERNAL_APPROXIMATE);
494 message.setData(bundle);
495 mUpdateHandler.sendMessage(message);
499 public void updateApproximateInternal(Bundle bundle) {
500 final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_APPROXIMATE);
501 message.setData(bundle);
502 mUpdateHandler.sendMessage(message);
506 public void updateExactInternal(Bundle bundle) {
507 final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_EXACT);
508 message.setData(bundle);
509 mUpdateHandler.sendMessage(message);