OSDN Git Service

am 99b6deb1: am fdf7220a: bug:3308791 appusage should include space used on /sdcard
[android-x86/packages-apps-Settings.git] / src / com / android / settings / deviceinfo / Memory.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.settings.deviceinfo;
18
19 import com.android.settings.R;
20 import com.android.settings.SettingsPreferenceFragment;
21 import com.android.settings.deviceinfo.MemoryMeasurement.MeasurementReceiver;
22
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;
54
55 import java.util.List;
56
57 public class Memory extends SettingsPreferenceFragment implements OnCancelListener,
58         MeasurementReceiver {
59     private static final String TAG = "Memory";
60     static final boolean localLOGV = false;
61
62     private static final String MEMORY_SD_SIZE = "memory_sd_size";
63
64     private static final String MEMORY_SD_AVAIL = "memory_sd_avail";
65
66     private static final String MEMORY_SD_MOUNT_TOGGLE = "memory_sd_mount_toggle";
67
68     private static final String MEMORY_SD_FORMAT = "memory_sd_format";
69
70     private static final String MEMORY_SD_GROUP = "memory_sd";
71
72     private static final String MEMORY_INTERNAL_SIZE = "memory_internal_size";
73
74     private static final String MEMORY_INTERNAL_AVAIL = "memory_internal_avail";
75
76     private static final String MEMORY_INTERNAL_APPS = "memory_internal_apps";
77
78     private static final String MEMORY_INTERNAL_MEDIA = "memory_internal_media";
79
80     private static final String MEMORY_INTERNAL_CHART = "memory_internal_chart";
81
82     private static final int DLG_CONFIRM_UNMOUNT = 1;
83     private static final int DLG_ERROR_UNMOUNT = 2;
84
85     private Resources mRes;
86
87     // External storage preferences
88     private Preference mSdSize;
89     private Preference mSdAvail;
90     private Preference mSdMountToggle;
91     private Preference mSdFormat;
92     private PreferenceGroup mSdMountPreferenceGroup;
93
94     // Internal storage preferences
95     private Preference mInternalSize;
96     private Preference mInternalAvail;
97     private Preference mInternalMediaUsage;
98     private Preference mInternalAppsUsage;
99     private UsageBarPreference mInternalUsageChart;
100
101     // Internal storage chart colors
102     private int mInternalMediaColor;
103     private int mInternalAppsColor;
104     private int mInternalUsedColor;
105
106     boolean mSdMountToggleAdded = true;
107     
108     // Access using getMountService()
109     private IMountService mMountService = null;
110
111     private StorageManager mStorageManager = null;
112
113     // Updates the memory usage bar graph.
114     private static final int MSG_UI_UPDATE_INTERNAL_APPROXIMATE = 1;
115
116     // Updates the memory usage bar graph.
117     private static final int MSG_UI_UPDATE_INTERNAL_EXACT = 2;
118
119     // Updates the memory usage stats for external.
120     private static final int MSG_UI_UPDATE_EXTERNAL_APPROXIMATE = 3;
121
122     private Handler mUpdateHandler = new Handler() {
123         @Override
124         public void handleMessage(Message msg) {
125             switch (msg.what) {
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);
131                     break;
132                 }
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);
140                     break;
141                 }
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);
147                     break;
148                 }
149             }
150         }
151     };
152
153     private MemoryMeasurement mMeasurement;
154
155     @Override
156     public void onCreate(Bundle icicle) {
157         super.onCreate(icicle);
158
159         if (mStorageManager == null) {
160             mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
161             mStorageManager.registerListener(mStorageListener);
162         }
163
164         addPreferencesFromResource(R.xml.device_info_memory);
165
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);
172
173         if (Environment.isExternalStorageEmulated()) {
174             mSdMountPreferenceGroup.removePreference(mSdSize);
175             mSdMountPreferenceGroup.removePreference(mSdAvail);
176             mSdMountPreferenceGroup.removePreference(mSdMountToggle);
177         }
178
179         mInternalSize = findPreference(MEMORY_INTERNAL_SIZE);
180         mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL);
181         mInternalMediaUsage = findPreference(MEMORY_INTERNAL_MEDIA);
182         mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS);
183
184         mInternalMediaColor = mRes.getColor(R.color.memory_media_usage);
185         mInternalAppsColor = mRes.getColor(R.color.memory_apps_usage);
186         mInternalUsedColor = mRes.getColor(R.color.memory_used);
187
188         float[] radius = new float[] {
189                 5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f
190         };
191         RoundRectShape shape1 = new RoundRectShape(radius, null, null);
192
193         ShapeDrawable mediaShape = new ShapeDrawable(shape1);
194         mediaShape.setIntrinsicWidth(32);
195         mediaShape.setIntrinsicHeight(32);
196         mediaShape.getPaint().setColor(mInternalMediaColor);
197         mInternalMediaUsage.setIcon(mediaShape);
198
199         ShapeDrawable appsShape = new ShapeDrawable(shape1);
200         appsShape.setIntrinsicWidth(32);
201         appsShape.setIntrinsicHeight(32);
202         appsShape.getPaint().setColor(mInternalAppsColor);
203         mInternalAppsUsage.setIcon(appsShape);
204
205         mInternalUsageChart = (UsageBarPreference) findPreference(MEMORY_INTERNAL_CHART);
206
207         mMeasurement = MemoryMeasurement.getInstance(getActivity());
208         mMeasurement.setReceiver(this);
209     }
210
211     @Override
212     public void onResume() {
213         super.onResume();
214
215         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
216         intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
217         intentFilter.addDataScheme("file");
218         getActivity().registerReceiver(mReceiver, intentFilter);
219
220         mMeasurement.invalidate();
221         if (!Environment.isExternalStorageEmulated()) {
222             mMeasurement.measureExternal();
223         }
224         mMeasurement.measureInternal();
225     }
226
227     StorageEventListener mStorageListener = new StorageEventListener() {
228         @Override
229         public void onStorageStateChanged(String path, String oldState, String newState) {
230             Log.i(TAG, "Received storage state changed notification that " +
231                     path + " changed state from " + oldState +
232                     " to " + newState);
233             if (!Environment.isExternalStorageEmulated()) {
234                 mMeasurement.measureExternal();
235             }
236         }
237     };
238
239     @Override
240     public void onPause() {
241         super.onPause();
242         getActivity().unregisterReceiver(mReceiver);
243         mMeasurement.cleanUp();
244     }
245
246     @Override
247     public void onDestroy() {
248         if (mStorageManager != null && mStorageListener != null) {
249             mStorageManager.unregisterListener(mStorageListener);
250         }
251         super.onDestroy();
252     }
253
254     private synchronized IMountService getMountService() {
255        if (mMountService == null) {
256            IBinder service = ServiceManager.getService("mount");
257            if (service != null) {
258                mMountService = IMountService.Stub.asInterface(service);
259            } else {
260                Log.e(TAG, "Can't get mount service");
261            }
262        }
263        return mMountService;
264     }
265     
266     @Override
267     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
268         if (preference == mSdMountToggle) {
269             String status = Environment.getExternalStorageState();
270             if (status.equals(Environment.MEDIA_MOUNTED)) {
271                 unmount();
272             } else {
273                 mount();
274             }
275             return true;
276         } else if (preference == mSdFormat) {
277             Intent intent = new Intent(Intent.ACTION_VIEW);
278             intent.setClass(getActivity(), com.android.settings.MediaFormat.class);
279             startActivity(intent);
280             return true;
281         } else if (preference == mInternalAppsUsage) {
282             Intent intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
283             intent.setClass(getActivity(),
284                     com.android.settings.Settings.ManageApplicationsActivity.class);
285             startActivity(intent);
286             return true;
287         }
288
289         return false;
290     }
291      
292     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
293         @Override
294         public void onReceive(Context context, Intent intent) {
295             mMeasurement.invalidate();
296
297             if (!Environment.isExternalStorageEmulated()) {
298                 mMeasurement.measureExternal();
299             }
300             mMeasurement.measureInternal();
301         }
302     };
303
304     @Override
305     public Dialog onCreateDialog(int id) {
306         switch (id) {
307         case DLG_CONFIRM_UNMOUNT:
308                 return new AlertDialog.Builder(getActivity())
309                     .setTitle(R.string.dlg_confirm_unmount_title)
310                     .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
311                         public void onClick(DialogInterface dialog, int which) {
312                             doUnmount(true);
313                         }})
314                     .setNegativeButton(R.string.cancel, null)
315                     .setMessage(R.string.dlg_confirm_unmount_text)
316                     .setOnCancelListener(this)
317                     .create();
318         case DLG_ERROR_UNMOUNT:
319                 return new AlertDialog.Builder(getActivity())
320             .setTitle(R.string.dlg_error_unmount_title)
321             .setNeutralButton(R.string.dlg_ok, null)
322             .setMessage(R.string.dlg_error_unmount_text)
323             .setOnCancelListener(this)
324             .create();
325         }
326         return null;
327     }
328
329     private void doUnmount(boolean force) {
330         // Present a toast here
331         Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
332         IMountService mountService = getMountService();
333         String extStoragePath = Environment.getExternalStorageDirectory().toString();
334         try {
335             mSdMountToggle.setEnabled(false);
336             mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title));
337             mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary));
338             mountService.unmountVolume(extStoragePath, force);
339         } catch (RemoteException e) {
340             // Informative dialog to user that
341             // unmount failed.
342             showDialogInner(DLG_ERROR_UNMOUNT);
343         }
344     }
345
346     private void showDialogInner(int id) {
347         removeDialog(id);
348         showDialog(id);
349     }
350
351     private boolean hasAppsAccessingStorage() throws RemoteException {
352         String extStoragePath = Environment.getExternalStorageDirectory().toString();
353         IMountService mountService = getMountService();
354         int stUsers[] = mountService.getStorageUsers(extStoragePath);
355         if (stUsers != null && stUsers.length > 0) {
356             return true;
357         }
358         ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
359         List<ApplicationInfo> list = am.getRunningExternalApplications();
360         if (list != null && list.size() > 0) {
361             return true;
362         }
363         return false;
364     }
365
366     private void unmount() {
367         // Check if external media is in use.
368         try {
369            if (hasAppsAccessingStorage()) {
370                if (localLOGV) Log.i(TAG, "Do have storage users accessing media");
371                // Present dialog to user
372                showDialogInner(DLG_CONFIRM_UNMOUNT);
373            } else {
374                doUnmount(true);
375            }
376         } catch (RemoteException e) {
377             // Very unlikely. But present an error dialog anyway
378             Log.e(TAG, "Is MountService running?");
379             showDialogInner(DLG_ERROR_UNMOUNT);
380         }
381     }
382
383     private void mount() {
384         IMountService mountService = getMountService();
385         try {
386             if (mountService != null) {
387                 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
388             } else {
389                 Log.e(TAG, "Mount service is null, can't mount");
390             }
391         } catch (RemoteException ex) {
392         }
393     }
394
395     private void updateUiExact(long totalSize, long availSize, long mediaSize, long appsSize) {
396         mInternalSize.setSummary(formatSize(totalSize));
397         mInternalAvail.setSummary(formatSize(availSize));
398         mInternalMediaUsage.setSummary(formatSize(mediaSize));
399         mInternalAppsUsage.setSummary(formatSize(appsSize));
400
401         mInternalUsageChart.clear();
402         mInternalUsageChart.addEntry(mediaSize / (float) totalSize, mInternalMediaColor);
403         mInternalUsageChart.addEntry(appsSize / (float) totalSize, mInternalAppsColor);
404
405         final long usedSize = totalSize - availSize;
406
407         // There are other things that can take up storage, but we didn't
408         // measure it.
409         final long remaining = usedSize - (mediaSize + appsSize);
410         if (remaining > 0) {
411             mInternalUsageChart.addEntry(remaining / (float) totalSize, mInternalUsedColor);
412         }
413         mInternalUsageChart.commit();
414     }
415
416     private void updateUiApproximate(long totalSize, long availSize) {
417         mInternalSize.setSummary(formatSize(totalSize));
418         mInternalAvail.setSummary(formatSize(availSize));
419
420         final long usedSize = totalSize - availSize;
421
422         mInternalUsageChart.clear();
423         mInternalUsageChart.addEntry(usedSize / (float) totalSize, mInternalUsedColor);
424         mInternalUsageChart.commit();
425     }
426
427     private void updateExternalStorage(long totalSize, long availSize) {
428         String status = Environment.getExternalStorageState();
429         String readOnly = "";
430         if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
431             status = Environment.MEDIA_MOUNTED;
432             readOnly = mRes.getString(R.string.read_only);
433         }
434
435         if (status.equals(Environment.MEDIA_MOUNTED)) {
436             if (!Environment.isExternalStorageRemovable()) {
437                 // This device has built-in storage that is not removable.
438                 // There is no reason for the user to unmount it.
439                 if (mSdMountToggleAdded) {
440                     mSdMountPreferenceGroup.removePreference(mSdMountToggle);
441                     mSdMountToggleAdded = false;
442                 }
443             }
444             try {
445                 mSdSize.setSummary(formatSize(totalSize));
446                 mSdAvail.setSummary(formatSize(availSize) + readOnly);
447
448                 mSdMountToggle.setEnabled(true);
449                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
450                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
451
452             } catch (IllegalArgumentException e) {
453                 // this can occur if the SD card is removed, but we haven't
454                 // received the
455                 // ACTION_MEDIA_REMOVED Intent yet.
456                 status = Environment.MEDIA_REMOVED;
457             }
458         } else {
459             mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
460             mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
461
462             if (!Environment.isExternalStorageRemovable()) {
463                 if (status.equals(Environment.MEDIA_UNMOUNTED)) {
464                     if (!mSdMountToggleAdded) {
465                         mSdMountPreferenceGroup.addPreference(mSdMountToggle);
466                         mSdMountToggleAdded = true;
467                     }
468                 }
469             }
470
471             if (status.equals(Environment.MEDIA_UNMOUNTED) || status.equals(Environment.MEDIA_NOFS)
472                     || status.equals(Environment.MEDIA_UNMOUNTABLE)) {
473                 mSdMountToggle.setEnabled(true);
474                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
475                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
476             } else {
477                 mSdMountToggle.setEnabled(false);
478                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
479                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
480             }
481         }
482     }
483
484     private String formatSize(long size) {
485         return Formatter.formatFileSize(getActivity(), size);
486     }
487
488     public void onCancel(DialogInterface dialog) {
489         // TODO: Is this really required?
490         // finish();
491     }
492
493     @Override
494     public void updateApproximateExternal(Bundle bundle) {
495         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXTERNAL_APPROXIMATE);
496         message.setData(bundle);
497         mUpdateHandler.sendMessage(message);
498     }
499
500     @Override
501     public void updateApproximateInternal(Bundle bundle) {
502         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_APPROXIMATE);
503         message.setData(bundle);
504         mUpdateHandler.sendMessage(message);
505     }
506
507     @Override
508     public void updateExactInternal(Bundle bundle) {
509         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_EXACT);
510         message.setData(bundle);
511         mUpdateHandler.sendMessage(message);
512     }
513 }