OSDN Git Service

Merge "Recheck the storage sizes when Storage settings is resumed."
[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     private 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         }
282
283         return false;
284     }
285      
286     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
287         @Override
288         public void onReceive(Context context, Intent intent) {
289             mMeasurement.invalidate();
290
291             if (!Environment.isExternalStorageEmulated()) {
292                 mMeasurement.measureExternal();
293             }
294             mMeasurement.measureInternal();
295         }
296     };
297
298     @Override
299     public Dialog onCreateDialog(int id) {
300         switch (id) {
301         case DLG_CONFIRM_UNMOUNT:
302                 return new AlertDialog.Builder(getActivity())
303                     .setTitle(R.string.dlg_confirm_unmount_title)
304                     .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
305                         public void onClick(DialogInterface dialog, int which) {
306                             doUnmount(true);
307                         }})
308                     .setNegativeButton(R.string.cancel, null)
309                     .setMessage(R.string.dlg_confirm_unmount_text)
310                     .setOnCancelListener(this)
311                     .create();
312         case DLG_ERROR_UNMOUNT:
313                 return new AlertDialog.Builder(getActivity())
314             .setTitle(R.string.dlg_error_unmount_title)
315             .setNeutralButton(R.string.dlg_ok, null)
316             .setMessage(R.string.dlg_error_unmount_text)
317             .setOnCancelListener(this)
318             .create();
319         }
320         return null;
321     }
322
323     private void doUnmount(boolean force) {
324         // Present a toast here
325         Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
326         IMountService mountService = getMountService();
327         String extStoragePath = Environment.getExternalStorageDirectory().toString();
328         try {
329             mSdMountToggle.setEnabled(false);
330             mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title));
331             mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary));
332             mountService.unmountVolume(extStoragePath, force);
333         } catch (RemoteException e) {
334             // Informative dialog to user that
335             // unmount failed.
336             showDialogInner(DLG_ERROR_UNMOUNT);
337         }
338     }
339
340     private void showDialogInner(int id) {
341         removeDialog(id);
342         showDialog(id);
343     }
344
345     private boolean hasAppsAccessingStorage() throws RemoteException {
346         String extStoragePath = Environment.getExternalStorageDirectory().toString();
347         IMountService mountService = getMountService();
348         int stUsers[] = mountService.getStorageUsers(extStoragePath);
349         if (stUsers != null && stUsers.length > 0) {
350             return true;
351         }
352         ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
353         List<ApplicationInfo> list = am.getRunningExternalApplications();
354         if (list != null && list.size() > 0) {
355             return true;
356         }
357         return false;
358     }
359
360     private void unmount() {
361         // Check if external media is in use.
362         try {
363            if (hasAppsAccessingStorage()) {
364                if (localLOGV) Log.i(TAG, "Do have storage users accessing media");
365                // Present dialog to user
366                showDialogInner(DLG_CONFIRM_UNMOUNT);
367            } else {
368                doUnmount(true);
369            }
370         } catch (RemoteException e) {
371             // Very unlikely. But present an error dialog anyway
372             Log.e(TAG, "Is MountService running?");
373             showDialogInner(DLG_ERROR_UNMOUNT);
374         }
375     }
376
377     private void mount() {
378         IMountService mountService = getMountService();
379         try {
380             if (mountService != null) {
381                 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
382             } else {
383                 Log.e(TAG, "Mount service is null, can't mount");
384             }
385         } catch (RemoteException ex) {
386         }
387     }
388
389     private void updateUiExact(long totalSize, long availSize, long mediaSize, long appsSize) {
390         mInternalSize.setSummary(formatSize(totalSize));
391         mInternalAvail.setSummary(formatSize(availSize));
392         mInternalMediaUsage.setSummary(formatSize(mediaSize));
393         mInternalAppsUsage.setSummary(formatSize(appsSize));
394
395         mInternalUsageChart.clear();
396         mInternalUsageChart.addEntry(mediaSize / (float) totalSize, mInternalMediaColor);
397         mInternalUsageChart.addEntry(appsSize / (float) totalSize, mInternalAppsColor);
398
399         final long usedSize = totalSize - availSize;
400
401         // There are other things that can take up storage, but we didn't
402         // measure it.
403         final long remaining = usedSize - (mediaSize + appsSize);
404         if (remaining > 0) {
405             mInternalUsageChart.addEntry(remaining / (float) totalSize, mInternalUsedColor);
406         }
407         mInternalUsageChart.commit();
408     }
409
410     private void updateUiApproximate(long totalSize, long availSize) {
411         mInternalSize.setSummary(formatSize(totalSize));
412         mInternalAvail.setSummary(formatSize(availSize));
413
414         final long usedSize = totalSize - availSize;
415
416         mInternalUsageChart.clear();
417         mInternalUsageChart.addEntry(usedSize / (float) totalSize, mInternalUsedColor);
418         mInternalUsageChart.commit();
419     }
420
421     private void updateExternalStorage(long totalSize, long availSize) {
422         String status = Environment.getExternalStorageState();
423         String readOnly = "";
424         if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
425             status = Environment.MEDIA_MOUNTED;
426             readOnly = mRes.getString(R.string.read_only);
427         }
428
429         if (status.equals(Environment.MEDIA_MOUNTED)) {
430             if (!Environment.isExternalStorageRemovable()) {
431                 // This device has built-in storage that is not removable.
432                 // There is no reason for the user to unmount it.
433                 if (mSdMountToggleAdded) {
434                     mSdMountPreferenceGroup.removePreference(mSdMountToggle);
435                     mSdMountToggleAdded = false;
436                 }
437             }
438             try {
439                 mSdSize.setSummary(formatSize(totalSize));
440                 mSdAvail.setSummary(formatSize(availSize) + readOnly);
441
442                 mSdMountToggle.setEnabled(true);
443                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
444                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
445
446             } catch (IllegalArgumentException e) {
447                 // this can occur if the SD card is removed, but we haven't
448                 // received the
449                 // ACTION_MEDIA_REMOVED Intent yet.
450                 status = Environment.MEDIA_REMOVED;
451             }
452         } else {
453             mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
454             mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
455
456             if (!Environment.isExternalStorageRemovable()) {
457                 if (status.equals(Environment.MEDIA_UNMOUNTED)) {
458                     if (!mSdMountToggleAdded) {
459                         mSdMountPreferenceGroup.addPreference(mSdMountToggle);
460                         mSdMountToggleAdded = true;
461                     }
462                 }
463             }
464
465             if (status.equals(Environment.MEDIA_UNMOUNTED) || status.equals(Environment.MEDIA_NOFS)
466                     || status.equals(Environment.MEDIA_UNMOUNTABLE)) {
467                 mSdMountToggle.setEnabled(true);
468                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
469                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
470             } else {
471                 mSdMountToggle.setEnabled(false);
472                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
473                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
474             }
475         }
476     }
477
478     private String formatSize(long size) {
479         return Formatter.formatFileSize(getActivity(), size);
480     }
481
482     public void onCancel(DialogInterface dialog) {
483         // TODO: Is this really required?
484         // finish();
485     }
486
487     @Override
488     public void updateApproximateExternal(Bundle bundle) {
489         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXTERNAL_APPROXIMATE);
490         message.setData(bundle);
491         mUpdateHandler.sendMessage(message);
492     }
493
494     @Override
495     public void updateApproximateInternal(Bundle bundle) {
496         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_APPROXIMATE);
497         message.setData(bundle);
498         mUpdateHandler.sendMessage(message);
499     }
500
501     @Override
502     public void updateExactInternal(Bundle bundle) {
503         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_EXACT);
504         message.setData(bundle);
505         mUpdateHandler.sendMessage(message);
506     }
507 }