OSDN Git Service

Merge "Use expander assets for bluetooth profile preference." into honeycomb
[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             getPreferenceScreen().removePreference(mSdMountPreferenceGroup);
175         }
176
177         mInternalSize = findPreference(MEMORY_INTERNAL_SIZE);
178         mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL);
179         mInternalMediaUsage = findPreference(MEMORY_INTERNAL_MEDIA);
180         mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS);
181
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);
185
186         float[] radius = new float[] {
187                 5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f
188         };
189         RoundRectShape shape1 = new RoundRectShape(radius, null, null);
190
191         ShapeDrawable mediaShape = new ShapeDrawable(shape1);
192         mediaShape.setIntrinsicWidth(32);
193         mediaShape.setIntrinsicHeight(32);
194         mediaShape.getPaint().setColor(mInternalMediaColor);
195         mInternalMediaUsage.setIcon(mediaShape);
196
197         ShapeDrawable appsShape = new ShapeDrawable(shape1);
198         appsShape.setIntrinsicWidth(32);
199         appsShape.setIntrinsicHeight(32);
200         appsShape.getPaint().setColor(mInternalAppsColor);
201         mInternalAppsUsage.setIcon(appsShape);
202
203         mInternalUsageChart = (UsageBarPreference) findPreference(MEMORY_INTERNAL_CHART);
204
205         mMeasurement = MemoryMeasurement.getInstance(getActivity());
206         mMeasurement.setReceiver(this);
207     }
208
209     @Override
210     public void onResume() {
211         super.onResume();
212
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);
217
218         mMeasurement.invalidate();
219         if (!Environment.isExternalStorageEmulated()) {
220             mMeasurement.measureExternal();
221         }
222         mMeasurement.measureInternal();
223     }
224
225     StorageEventListener mStorageListener = new StorageEventListener() {
226         @Override
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 +
230                     " to " + newState);
231             if (!Environment.isExternalStorageEmulated()) {
232                 mMeasurement.measureExternal();
233             }
234         }
235     };
236
237     @Override
238     public void onPause() {
239         super.onPause();
240         getActivity().unregisterReceiver(mReceiver);
241         mMeasurement.cleanUp();
242     }
243
244     @Override
245     public void onDestroy() {
246         if (mStorageManager != null && mStorageListener != null) {
247             mStorageManager.unregisterListener(mStorageListener);
248         }
249         super.onDestroy();
250     }
251
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);
257            } else {
258                Log.e(TAG, "Can't get mount service");
259            }
260        }
261        return mMountService;
262     }
263     
264     @Override
265     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
266         if (preference == mSdMountToggle) {
267             String status = Environment.getExternalStorageState();
268             if (status.equals(Environment.MEDIA_MOUNTED)) {
269                 unmount();
270             } else {
271                 mount();
272             }
273             return true;
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);
278             return true;
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);
284             return true;
285         }
286
287         return false;
288     }
289      
290     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
291         @Override
292         public void onReceive(Context context, Intent intent) {
293             mMeasurement.invalidate();
294
295             if (!Environment.isExternalStorageEmulated()) {
296                 mMeasurement.measureExternal();
297             }
298             mMeasurement.measureInternal();
299         }
300     };
301
302     @Override
303     public Dialog onCreateDialog(int id) {
304         switch (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) {
310                             doUnmount(true);
311                         }})
312                     .setNegativeButton(R.string.cancel, null)
313                     .setMessage(R.string.dlg_confirm_unmount_text)
314                     .create();
315         case DLG_ERROR_UNMOUNT:
316                 return new AlertDialog.Builder(getActivity())
317             .setTitle(R.string.dlg_error_unmount_title)
318             .setNeutralButton(R.string.dlg_ok, null)
319             .setMessage(R.string.dlg_error_unmount_text)
320             .create();
321         }
322         return null;
323     }
324
325     @Override
326     protected void showDialog(int id) {
327         super.showDialog(id);
328
329         switch (id) {
330         case DLG_CONFIRM_UNMOUNT:
331         case DLG_ERROR_UNMOUNT:
332             setOnCancelListener(this);
333             break;
334         }
335     }
336
337     private void doUnmount(boolean force) {
338         // Present a toast here
339         Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
340         IMountService mountService = getMountService();
341         String extStoragePath = Environment.getExternalStorageDirectory().toString();
342         try {
343             mSdMountToggle.setEnabled(false);
344             mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title));
345             mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary));
346             mountService.unmountVolume(extStoragePath, force);
347         } catch (RemoteException e) {
348             // Informative dialog to user that
349             // unmount failed.
350             showDialogInner(DLG_ERROR_UNMOUNT);
351         }
352     }
353
354     private void showDialogInner(int id) {
355         removeDialog(id);
356         showDialog(id);
357     }
358
359     private boolean hasAppsAccessingStorage() throws RemoteException {
360         String extStoragePath = Environment.getExternalStorageDirectory().toString();
361         IMountService mountService = getMountService();
362         int stUsers[] = mountService.getStorageUsers(extStoragePath);
363         if (stUsers != null && stUsers.length > 0) {
364             return true;
365         }
366         ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
367         List<ApplicationInfo> list = am.getRunningExternalApplications();
368         if (list != null && list.size() > 0) {
369             return true;
370         }
371         return false;
372     }
373
374     private void unmount() {
375         // Check if external media is in use.
376         try {
377            if (hasAppsAccessingStorage()) {
378                if (localLOGV) Log.i(TAG, "Do have storage users accessing media");
379                // Present dialog to user
380                showDialogInner(DLG_CONFIRM_UNMOUNT);
381            } else {
382                doUnmount(true);
383            }
384         } catch (RemoteException e) {
385             // Very unlikely. But present an error dialog anyway
386             Log.e(TAG, "Is MountService running?");
387             showDialogInner(DLG_ERROR_UNMOUNT);
388         }
389     }
390
391     private void mount() {
392         IMountService mountService = getMountService();
393         try {
394             if (mountService != null) {
395                 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
396             } else {
397                 Log.e(TAG, "Mount service is null, can't mount");
398             }
399         } catch (RemoteException ex) {
400         }
401     }
402
403     private void updateUiExact(long totalSize, long availSize, long mediaSize, long appsSize) {
404         mInternalSize.setSummary(formatSize(totalSize));
405         mInternalAvail.setSummary(formatSize(availSize));
406         mInternalMediaUsage.setSummary(formatSize(mediaSize));
407         mInternalAppsUsage.setSummary(formatSize(appsSize));
408
409         mInternalUsageChart.clear();
410         mInternalUsageChart.addEntry(mediaSize / (float) totalSize, mInternalMediaColor);
411         mInternalUsageChart.addEntry(appsSize / (float) totalSize, mInternalAppsColor);
412
413         final long usedSize = totalSize - availSize;
414
415         // There are other things that can take up storage, but we didn't
416         // measure it.
417         final long remaining = usedSize - (mediaSize + appsSize);
418         if (remaining > 0) {
419             mInternalUsageChart.addEntry(remaining / (float) totalSize, mInternalUsedColor);
420         }
421         mInternalUsageChart.commit();
422     }
423
424     private void updateUiApproximate(long totalSize, long availSize) {
425         mInternalSize.setSummary(formatSize(totalSize));
426         mInternalAvail.setSummary(formatSize(availSize));
427
428         final long usedSize = totalSize - availSize;
429
430         mInternalUsageChart.clear();
431         mInternalUsageChart.addEntry(usedSize / (float) totalSize, mInternalUsedColor);
432         mInternalUsageChart.commit();
433     }
434
435     private void updateExternalStorage(long totalSize, long availSize) {
436         String status = Environment.getExternalStorageState();
437         String readOnly = "";
438         if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
439             status = Environment.MEDIA_MOUNTED;
440             readOnly = mRes.getString(R.string.read_only);
441         }
442
443         if (status.equals(Environment.MEDIA_MOUNTED)) {
444             if (!Environment.isExternalStorageRemovable()) {
445                 // This device has built-in storage that is not removable.
446                 // There is no reason for the user to unmount it.
447                 if (mSdMountToggleAdded) {
448                     mSdMountPreferenceGroup.removePreference(mSdMountToggle);
449                     mSdMountToggleAdded = false;
450                 }
451             }
452             try {
453                 mSdSize.setSummary(formatSize(totalSize));
454                 mSdAvail.setSummary(formatSize(availSize) + readOnly);
455
456                 mSdMountToggle.setEnabled(true);
457                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
458                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
459
460             } catch (IllegalArgumentException e) {
461                 // this can occur if the SD card is removed, but we haven't
462                 // received the
463                 // ACTION_MEDIA_REMOVED Intent yet.
464                 status = Environment.MEDIA_REMOVED;
465             }
466         } else {
467             mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
468             mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
469
470             if (!Environment.isExternalStorageRemovable()) {
471                 if (status.equals(Environment.MEDIA_UNMOUNTED)) {
472                     if (!mSdMountToggleAdded) {
473                         mSdMountPreferenceGroup.addPreference(mSdMountToggle);
474                         mSdMountToggleAdded = true;
475                     }
476                 }
477             }
478
479             if (status.equals(Environment.MEDIA_UNMOUNTED) || status.equals(Environment.MEDIA_NOFS)
480                     || status.equals(Environment.MEDIA_UNMOUNTABLE)) {
481                 mSdMountToggle.setEnabled(true);
482                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
483                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
484             } else {
485                 mSdMountToggle.setEnabled(false);
486                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
487                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
488             }
489         }
490     }
491
492     private String formatSize(long size) {
493         return Formatter.formatFileSize(getActivity(), size);
494     }
495
496     public void onCancel(DialogInterface dialog) {
497         // TODO: Is this really required?
498         // finish();
499     }
500
501     @Override
502     public void updateApproximateExternal(Bundle bundle) {
503         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXTERNAL_APPROXIMATE);
504         message.setData(bundle);
505         mUpdateHandler.sendMessage(message);
506     }
507
508     @Override
509     public void updateApproximateInternal(Bundle bundle) {
510         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_APPROXIMATE);
511         message.setData(bundle);
512         mUpdateHandler.sendMessage(message);
513     }
514
515     @Override
516     public void updateExactInternal(Bundle bundle) {
517         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_EXACT);
518         message.setData(bundle);
519         mUpdateHandler.sendMessage(message);
520     }
521 }