OSDN Git Service

Merge "Changes for ril.h version 6."
[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.app.DownloadManager;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.DialogInterface;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.DialogInterface.OnCancelListener;
33 import android.content.pm.ApplicationInfo;
34 import android.content.res.Resources;
35 import android.graphics.drawable.ShapeDrawable;
36 import android.graphics.drawable.shapes.RoundRectShape;
37 import android.os.Bundle;
38 import android.os.Environment;
39 import android.os.Handler;
40 import android.os.IBinder;
41 import android.os.Message;
42 import android.os.RemoteException;
43 import android.os.ServiceManager;
44 import android.os.storage.IMountService;
45 import android.os.storage.StorageEventListener;
46 import android.os.storage.StorageManager;
47 import android.preference.Preference;
48 import android.preference.PreferenceGroup;
49 import android.preference.PreferenceScreen;
50 import android.text.format.Formatter;
51 import android.util.Log;
52 import android.widget.Toast;
53
54 import java.util.List;
55
56 public class Memory extends SettingsPreferenceFragment implements OnCancelListener,
57         MeasurementReceiver {
58     private static final String TAG = "MemorySettings";
59
60     private static final String MEMORY_SD_SIZE = "memory_sd_size";
61
62     private static final String MEMORY_SD_AVAIL = "memory_sd_avail";
63
64     private static final String MEMORY_SD_MOUNT_TOGGLE = "memory_sd_mount_toggle";
65
66     private static final String MEMORY_SD_FORMAT = "memory_sd_format";
67
68     private static final String MEMORY_SD_GROUP = "memory_sd";
69
70     private static final String MEMORY_INTERNAL_SIZE = "memory_internal_size";
71
72     private static final String MEMORY_INTERNAL_AVAIL = "memory_internal_avail";
73
74     private static final String MEMORY_INTERNAL_APPS = "memory_internal_apps";
75
76     private static final String MEMORY_INTERNAL_CHART = "memory_internal_chart";
77
78     private static final int DLG_CONFIRM_UNMOUNT = 1;
79     private static final int DLG_ERROR_UNMOUNT = 2;
80
81     private Resources mRes;
82
83     // External storage preferences
84     private Preference mSdSize;
85     private Preference mSdAvail;
86     private Preference mSdMountToggle;
87     private Preference mSdFormat;
88     private PreferenceGroup mSdMountPreferenceGroup;
89
90     // Internal storage preferences
91     private Preference mInternalSize;
92     private Preference mInternalAvail;
93     private Preference mInternalAppsUsage;
94     private final Preference[] mMediaPreferences = new Preference[Constants.NUM_MEDIA_DIRS_TRACKED];
95     private UsageBarPreference mInternalUsageChart;
96
97     // Internal storage chart colors
98     private int mInternalAppsColor;
99     private int mInternalAvailColor;
100     private int mInternalUsedColor;
101
102     boolean mSdMountToggleAdded = true;
103     
104     // Access using getMountService()
105     private IMountService mMountService = null;
106
107     private StorageManager mStorageManager = null;
108
109     // Updates the memory usage bar graph.
110     private static final int MSG_UI_UPDATE_INTERNAL_APPROXIMATE = 1;
111
112     // Updates the memory usage bar graph.
113     private static final int MSG_UI_UPDATE_INTERNAL_EXACT = 2;
114
115     // Updates the memory usage stats for external.
116     private static final int MSG_UI_UPDATE_EXTERNAL_APPROXIMATE = 3;
117
118     private Handler mUpdateHandler = new Handler() {
119         @Override
120         public void handleMessage(Message msg) {
121             switch (msg.what) {
122                 case MSG_UI_UPDATE_INTERNAL_APPROXIMATE: {
123                     Bundle bundle = msg.getData();
124                     final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE);
125                     final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE);
126                     updateUiApproximate(totalSize, availSize);
127                     break;
128                 }
129                 case MSG_UI_UPDATE_INTERNAL_EXACT: {
130                     Bundle bundle = msg.getData();
131                     final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE);
132                     final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE);
133                     final long appsUsed = bundle.getLong(MemoryMeasurement.APPS_USED);
134                     final long[] mediaSizes = new long[Constants.NUM_MEDIA_DIRS_TRACKED];
135                     for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) {
136                         mediaSizes[i] = bundle.getLong(Constants.mMediaDirs.get(i).mKey);
137                     }
138                     updateUiExact(totalSize, availSize, appsUsed, mediaSizes);
139                     break;
140                 }
141                 case MSG_UI_UPDATE_EXTERNAL_APPROXIMATE: {
142                     Bundle bundle = msg.getData();
143                     final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE);
144                     final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE);
145                     updateExternalStorage(totalSize, availSize);
146                     break;
147                 }
148             }
149         }
150     };
151
152     private MemoryMeasurement mMeasurement;
153
154     @Override
155     public void onCreate(Bundle icicle) {
156         super.onCreate(icicle);
157
158         if (mStorageManager == null) {
159             mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
160             mStorageManager.registerListener(mStorageListener);
161         }
162
163         addPreferencesFromResource(R.xml.device_info_memory);
164
165         mRes = getResources();
166         mSdSize = findPreference(MEMORY_SD_SIZE);
167         mSdAvail = findPreference(MEMORY_SD_AVAIL);
168         mSdMountToggle = findPreference(MEMORY_SD_MOUNT_TOGGLE);
169         mSdFormat = findPreference(MEMORY_SD_FORMAT);
170         mSdMountPreferenceGroup = (PreferenceGroup)findPreference(MEMORY_SD_GROUP);
171
172         if (Environment.isExternalStorageEmulated()) {
173             getPreferenceScreen().removePreference(mSdMountPreferenceGroup);
174         }
175
176         mInternalSize = findPreference(MEMORY_INTERNAL_SIZE);
177         mInternalAppsColor = mRes.getColor(R.color.memory_apps_usage);
178         mInternalUsedColor = android.graphics.Color.GRAY;
179         mInternalAvailColor = mRes.getColor(R.color.memory_avail);
180         final int buttonSize = (int) mRes.getDimension(R.dimen.device_memory_usage_button_size);
181         float[] radius = new float[] {
182                 5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f
183         };
184         RoundRectShape shape1 = new RoundRectShape(radius, null, null);
185
186         // total available space
187         mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL);
188         ShapeDrawable availShape = new ShapeDrawable(shape1);
189         availShape.setIntrinsicWidth(buttonSize);
190         availShape.setIntrinsicHeight(buttonSize);
191         availShape.getPaint().setColor(mInternalAvailColor);
192         mInternalAvail.setIcon(availShape);
193
194         // used by apps
195         mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS);
196         ShapeDrawable appsShape = new ShapeDrawable(shape1);
197         appsShape.setIntrinsicWidth(buttonSize);
198         appsShape.setIntrinsicHeight(buttonSize);
199         appsShape.getPaint().setColor(mInternalAppsColor);
200         mInternalAppsUsage.setIcon(appsShape);
201
202         // space used by individual major directories on /sdcard
203         for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) {
204             // nothing to be displayed for certain entries in Constants.mMediaDirs
205             if (Constants.mMediaDirs.get(i).mPreferenceName == null) {
206                 continue;
207             }
208             mMediaPreferences[i] = findPreference(Constants.mMediaDirs.get(i).mPreferenceName);
209             ShapeDrawable shape = new ShapeDrawable(shape1);
210             shape.setIntrinsicWidth(buttonSize);
211             shape.setIntrinsicHeight(buttonSize);
212             int color = 0;
213             switch (i) {
214                 case Constants.DOWNLOADS_INDEX:
215                     color = mRes.getColor(R.color.memory_downloads);
216                     break;
217                 case Constants.PIC_VIDEO_INDEX:
218                     color = mRes.getColor(R.color.memory_video);
219                     break;
220                 case Constants.MUSIC_INDEX:
221                     color = mRes.getColor(R.color.memory_audio);
222                     break;
223                 case Constants.MEDIA_MISC_INDEX:
224                     color = mRes.getColor(R.color.memory_misc);
225                     break;
226             }
227             shape.getPaint().setColor(color);
228             mMediaPreferences[i].setIcon(shape);
229         }
230         mInternalUsageChart = (UsageBarPreference) findPreference(MEMORY_INTERNAL_CHART);
231
232         mMeasurement = MemoryMeasurement.getInstance(getActivity());
233         mMeasurement.setReceiver(this);
234     }
235
236     @Override
237     public void onResume() {
238         super.onResume();
239         mMeasurement.setReceiver(this);
240         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
241         intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
242         intentFilter.addDataScheme("file");
243         getActivity().registerReceiver(mReceiver, intentFilter);
244
245         mMeasurement.invalidate();
246         if (!Environment.isExternalStorageEmulated()) {
247             mMeasurement.measureExternal();
248         }
249         mMeasurement.measureInternal();
250     }
251
252     StorageEventListener mStorageListener = new StorageEventListener() {
253         @Override
254         public void onStorageStateChanged(String path, String oldState, String newState) {
255             Log.i(TAG, "Received storage state changed notification that " +
256                     path + " changed state from " + oldState +
257                     " to " + newState);
258             if (!Environment.isExternalStorageEmulated()) {
259                 mMeasurement.measureExternal();
260             }
261         }
262     };
263
264     @Override
265     public void onPause() {
266         super.onPause();
267         getActivity().unregisterReceiver(mReceiver);
268         mMeasurement.cleanUp();
269     }
270
271     @Override
272     public void onDestroy() {
273         if (mStorageManager != null && mStorageListener != null) {
274             mStorageManager.unregisterListener(mStorageListener);
275         }
276         super.onDestroy();
277     }
278
279     private synchronized IMountService getMountService() {
280        if (mMountService == null) {
281            IBinder service = ServiceManager.getService("mount");
282            if (service != null) {
283                mMountService = IMountService.Stub.asInterface(service);
284            } else {
285                Log.e(TAG, "Can't get mount service");
286            }
287        }
288        return mMountService;
289     }
290     
291     @Override
292     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
293         if (preference == mSdMountToggle) {
294             String status = Environment.getExternalStorageState();
295             if (status.equals(Environment.MEDIA_MOUNTED)) {
296                 unmount();
297             } else {
298                 mount();
299             }
300             return true;
301         } else if (preference == mSdFormat) {
302             Intent intent = new Intent(Intent.ACTION_VIEW);
303             intent.setClass(getActivity(), com.android.settings.MediaFormat.class);
304             startActivity(intent);
305             return true;
306         } else if (preference == mInternalAppsUsage) {
307             Intent intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
308             intent.setClass(getActivity(),
309                     com.android.settings.Settings.ManageApplicationsActivity.class);
310             startActivity(intent);
311             return true;
312         } else if (preference == mMediaPreferences[Constants.DOWNLOADS_INDEX]) {
313             Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
314                     .putExtra(DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
315             startActivity(intent);
316             return true;
317         } else if (preference == mMediaPreferences[Constants.MUSIC_INDEX]) {
318             Intent intent = new Intent("android.intent.action.GET_CONTENT");
319             intent.setType("audio/mp3");
320             startActivity(intent);
321             return true;
322         } else if (preference == mMediaPreferences[Constants.PIC_VIDEO_INDEX]) {
323             Intent intent = new Intent("android.intent.action.GET_CONTENT");
324             intent.setType("image/jpeg");
325             startActivity(intent);
326             return true;
327         } else if (preference == mMediaPreferences[Constants.MEDIA_MISC_INDEX]) {
328             Context context = getActivity().getApplicationContext();
329             if (MemoryMeasurement.getInstance(context).isSizeOfMiscCategorynonZero()) {
330                 startActivity(new Intent(context, MiscFilesHandler.class));
331             }
332             return true;
333         }
334
335         return false;
336     }
337      
338     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
339         @Override
340         public void onReceive(Context context, Intent intent) {
341             mMeasurement.invalidate();
342
343             if (!Environment.isExternalStorageEmulated()) {
344                 mMeasurement.measureExternal();
345             }
346             mMeasurement.measureInternal();
347         }
348     };
349
350     @Override
351     public Dialog onCreateDialog(int id) {
352         switch (id) {
353         case DLG_CONFIRM_UNMOUNT:
354                 return new AlertDialog.Builder(getActivity())
355                     .setTitle(R.string.dlg_confirm_unmount_title)
356                     .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
357                         public void onClick(DialogInterface dialog, int which) {
358                             doUnmount(true);
359                         }})
360                     .setNegativeButton(R.string.cancel, null)
361                     .setMessage(R.string.dlg_confirm_unmount_text)
362                     .create();
363         case DLG_ERROR_UNMOUNT:
364                 return new AlertDialog.Builder(getActivity())
365             .setTitle(R.string.dlg_error_unmount_title)
366             .setNeutralButton(R.string.dlg_ok, null)
367             .setMessage(R.string.dlg_error_unmount_text)
368             .create();
369         }
370         return null;
371     }
372
373     @Override
374     protected void showDialog(int id) {
375         super.showDialog(id);
376
377         switch (id) {
378         case DLG_CONFIRM_UNMOUNT:
379         case DLG_ERROR_UNMOUNT:
380             setOnCancelListener(this);
381             break;
382         }
383     }
384
385     private void doUnmount(boolean force) {
386         // Present a toast here
387         Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
388         IMountService mountService = getMountService();
389         String extStoragePath = Environment.getExternalStorageDirectory().toString();
390         try {
391             mSdMountToggle.setEnabled(false);
392             mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title));
393             mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary));
394             mountService.unmountVolume(extStoragePath, force);
395         } catch (RemoteException e) {
396             // Informative dialog to user that
397             // unmount failed.
398             showDialogInner(DLG_ERROR_UNMOUNT);
399         }
400     }
401
402     private void showDialogInner(int id) {
403         removeDialog(id);
404         showDialog(id);
405     }
406
407     private boolean hasAppsAccessingStorage() throws RemoteException {
408         String extStoragePath = Environment.getExternalStorageDirectory().toString();
409         IMountService mountService = getMountService();
410         int stUsers[] = mountService.getStorageUsers(extStoragePath);
411         if (stUsers != null && stUsers.length > 0) {
412             return true;
413         }
414         ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
415         List<ApplicationInfo> list = am.getRunningExternalApplications();
416         if (list != null && list.size() > 0) {
417             return true;
418         }
419         return false;
420     }
421
422     private void unmount() {
423         // Check if external media is in use.
424         try {
425            if (hasAppsAccessingStorage()) {
426                // Present dialog to user
427                showDialogInner(DLG_CONFIRM_UNMOUNT);
428            } else {
429                doUnmount(true);
430            }
431         } catch (RemoteException e) {
432             // Very unlikely. But present an error dialog anyway
433             Log.e(TAG, "Is MountService running?");
434             showDialogInner(DLG_ERROR_UNMOUNT);
435         }
436     }
437
438     private void mount() {
439         IMountService mountService = getMountService();
440         try {
441             if (mountService != null) {
442                 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
443             } else {
444                 Log.e(TAG, "Mount service is null, can't mount");
445             }
446         } catch (RemoteException ex) {
447         }
448     }
449
450     private void updateUiExact(long totalSize, long availSize, long appsSize, long[] mediaSizes) {
451         // There are other things that can take up storage, but we didn't measure it.
452         // add that unaccounted-for-usage to Apps Usage
453         long appsPlusRemaining = totalSize - availSize - mediaSizes[Constants.DOWNLOADS_INDEX] -
454                 mediaSizes[Constants.PIC_VIDEO_INDEX] - mediaSizes[Constants.MUSIC_INDEX] -
455                 mediaSizes[Constants.MEDIA_MISC_INDEX];
456         mInternalSize.setSummary(formatSize(totalSize));
457         mInternalAvail.setSummary(formatSize(availSize));
458         mInternalAppsUsage.setSummary(formatSize(appsPlusRemaining));
459
460         mInternalUsageChart.clear();
461         mInternalUsageChart.addEntry(appsPlusRemaining / (float) totalSize, mInternalAppsColor);
462
463         for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) {
464             if (Constants.mMediaDirs.get(i).mPreferenceName == null) {
465                 continue;
466             }
467             this.mMediaPreferences[i].setSummary(formatSize(mediaSizes[i]));
468             // don't add entry to color chart for media usage and for zero-sized dirs
469             if (i != Constants.MEDIA_INDEX && mediaSizes[i] > 0) {
470                 int color = 0;
471                 switch (i) {
472                     case Constants.DOWNLOADS_INDEX:
473                         color = mRes.getColor(R.color.memory_downloads);
474                         break;
475                     case Constants.PIC_VIDEO_INDEX:
476                         color = mRes.getColor(R.color.memory_video);
477                         break;
478                     case Constants.MUSIC_INDEX:
479                         color = mRes.getColor(R.color.memory_audio);
480                         break;
481                     case Constants.MEDIA_MISC_INDEX:
482                         color = mRes.getColor(R.color.memory_misc);
483                         break;
484                 }
485                 mInternalUsageChart.addEntry(mediaSizes[i] / (float) totalSize, color);
486             }
487         }
488         mInternalUsageChart.addEntry(availSize / (float) totalSize, mInternalAvailColor);
489         mInternalUsageChart.commit();
490     }
491
492     private void updateUiApproximate(long totalSize, long availSize) {
493         mInternalSize.setSummary(formatSize(totalSize));
494         mInternalAvail.setSummary(formatSize(availSize));
495
496         final long usedSize = totalSize - availSize;
497
498         mInternalUsageChart.clear();
499         mInternalUsageChart.addEntry(usedSize / (float) totalSize, mInternalUsedColor);
500         mInternalUsageChart.commit();
501     }
502
503     private void updateExternalStorage(long totalSize, long availSize) {
504         String status = Environment.getExternalStorageState();
505         String readOnly = "";
506         if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
507             status = Environment.MEDIA_MOUNTED;
508             readOnly = mRes.getString(R.string.read_only);
509         }
510
511         if (status.equals(Environment.MEDIA_MOUNTED)) {
512             if (!Environment.isExternalStorageRemovable()) {
513                 // This device has built-in storage that is not removable.
514                 // There is no reason for the user to unmount it.
515                 if (mSdMountToggleAdded) {
516                     mSdMountPreferenceGroup.removePreference(mSdMountToggle);
517                     mSdMountToggleAdded = false;
518                 }
519             }
520             try {
521                 mSdSize.setSummary(formatSize(totalSize));
522                 mSdAvail.setSummary(formatSize(availSize) + readOnly);
523
524                 mSdMountToggle.setEnabled(true);
525                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
526                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
527
528             } catch (IllegalArgumentException e) {
529                 // this can occur if the SD card is removed, but we haven't
530                 // received the
531                 // ACTION_MEDIA_REMOVED Intent yet.
532                 status = Environment.MEDIA_REMOVED;
533             }
534         } else {
535             mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
536             mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
537
538             if (!Environment.isExternalStorageRemovable()) {
539                 if (status.equals(Environment.MEDIA_UNMOUNTED)) {
540                     if (!mSdMountToggleAdded) {
541                         mSdMountPreferenceGroup.addPreference(mSdMountToggle);
542                         mSdMountToggleAdded = true;
543                     }
544                 }
545             }
546
547             if (status.equals(Environment.MEDIA_UNMOUNTED) || status.equals(Environment.MEDIA_NOFS)
548                     || status.equals(Environment.MEDIA_UNMOUNTABLE)) {
549                 mSdMountToggle.setEnabled(true);
550                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
551                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
552             } else {
553                 mSdMountToggle.setEnabled(false);
554                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
555                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
556             }
557         }
558     }
559
560     private String formatSize(long size) {
561         return Formatter.formatFileSize(getActivity(), size);
562     }
563
564     public void onCancel(DialogInterface dialog) {
565         // TODO: Is this really required?
566         // finish();
567     }
568
569     @Override
570     public void updateApproximateExternal(Bundle bundle) {
571         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXTERNAL_APPROXIMATE);
572         message.setData(bundle);
573         mUpdateHandler.sendMessage(message);
574     }
575
576     @Override
577     public void updateApproximateInternal(Bundle bundle) {
578         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_APPROXIMATE);
579         message.setData(bundle);
580         mUpdateHandler.sendMessage(message);
581     }
582
583     @Override
584     public void updateExactInternal(Bundle bundle) {
585         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_EXACT);
586         message.setData(bundle);
587         mUpdateHandler.sendMessage(message);
588     }
589 }