OSDN Git Service

Merge "Adding system wide long press timeout configurability under accessibility."
[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.RectShape;
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 buttonWidth = (int) mRes.getDimension(R.dimen.device_memory_usage_button_width);
181         final int buttonHeight = (int) mRes.getDimension(R.dimen.device_memory_usage_button_height);
182
183         // total available space
184         mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL);
185         mInternalAvail.setIcon(createRectShape(buttonHeight, buttonWidth, mInternalAvailColor));
186
187         // used by apps
188         mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS);
189         mInternalAppsUsage.setIcon(createRectShape(buttonHeight, buttonWidth, mInternalAppsColor));
190
191         // space used by individual major directories on /sdcard
192         for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) {
193             // nothing to be displayed for certain entries in Constants.mMediaDirs
194             if (Constants.mMediaDirs.get(i).mPreferenceName == null) {
195                 continue;
196             }
197             mMediaPreferences[i] = findPreference(Constants.mMediaDirs.get(i).mPreferenceName);
198             int color = 0;
199             switch (i) {
200                 case Constants.DOWNLOADS_INDEX:
201                     color = mRes.getColor(R.color.memory_downloads);
202                     break;
203                 case Constants.PIC_VIDEO_INDEX:
204                     color = mRes.getColor(R.color.memory_video);
205                     break;
206                 case Constants.MUSIC_INDEX:
207                     color = mRes.getColor(R.color.memory_audio);
208                     break;
209                 case Constants.MEDIA_MISC_INDEX:
210                     color = mRes.getColor(R.color.memory_misc);
211                     break;
212             }
213             mMediaPreferences[i].setIcon(createRectShape(buttonHeight, buttonWidth, color));
214         }
215         mInternalUsageChart = (UsageBarPreference) findPreference(MEMORY_INTERNAL_CHART);
216
217         mMeasurement = MemoryMeasurement.getInstance(getActivity());
218         mMeasurement.setReceiver(this);
219     }
220
221     private ShapeDrawable createRectShape(int height, int width, int color) {
222         ShapeDrawable shape = new ShapeDrawable(new RectShape());
223         shape.setIntrinsicHeight(height);
224         shape.setIntrinsicWidth(width);
225         shape.getPaint().setColor(color);
226         return shape;
227     }
228
229     @Override
230     public void onResume() {
231         super.onResume();
232         mMeasurement.setReceiver(this);
233         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
234         intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
235         intentFilter.addDataScheme("file");
236         getActivity().registerReceiver(mReceiver, intentFilter);
237
238         mMeasurement.invalidate();
239         if (!Environment.isExternalStorageEmulated()) {
240             mMeasurement.measureExternal();
241         }
242         mMeasurement.measureInternal();
243     }
244
245     StorageEventListener mStorageListener = new StorageEventListener() {
246         @Override
247         public void onStorageStateChanged(String path, String oldState, String newState) {
248             Log.i(TAG, "Received storage state changed notification that " +
249                     path + " changed state from " + oldState +
250                     " to " + newState);
251             if (!Environment.isExternalStorageEmulated()) {
252                 mMeasurement.measureExternal();
253             }
254         }
255     };
256
257     @Override
258     public void onPause() {
259         super.onPause();
260         getActivity().unregisterReceiver(mReceiver);
261         mMeasurement.cleanUp();
262     }
263
264     @Override
265     public void onDestroy() {
266         if (mStorageManager != null && mStorageListener != null) {
267             mStorageManager.unregisterListener(mStorageListener);
268         }
269         super.onDestroy();
270     }
271
272     private synchronized IMountService getMountService() {
273        if (mMountService == null) {
274            IBinder service = ServiceManager.getService("mount");
275            if (service != null) {
276                mMountService = IMountService.Stub.asInterface(service);
277            } else {
278                Log.e(TAG, "Can't get mount service");
279            }
280        }
281        return mMountService;
282     }
283     
284     @Override
285     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
286         if (preference == mSdMountToggle) {
287             String status = Environment.getExternalStorageState();
288             if (status.equals(Environment.MEDIA_MOUNTED)) {
289                 unmount();
290             } else {
291                 mount();
292             }
293             return true;
294         } else if (preference == mSdFormat) {
295             Intent intent = new Intent(Intent.ACTION_VIEW);
296             intent.setClass(getActivity(), com.android.settings.MediaFormat.class);
297             startActivity(intent);
298             return true;
299         } else if (preference == mInternalAppsUsage) {
300             Intent intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
301             intent.setClass(getActivity(),
302                     com.android.settings.Settings.ManageApplicationsActivity.class);
303             startActivity(intent);
304             return true;
305         } else if (preference == mMediaPreferences[Constants.DOWNLOADS_INDEX]) {
306             Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
307                     .putExtra(DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
308             startActivity(intent);
309             return true;
310         } else if (preference == mMediaPreferences[Constants.MUSIC_INDEX]) {
311             Intent intent = new Intent("android.intent.action.GET_CONTENT");
312             intent.setType("audio/mp3");
313             startActivity(intent);
314             return true;
315         } else if (preference == mMediaPreferences[Constants.PIC_VIDEO_INDEX]) {
316             Intent intent = new Intent("android.intent.action.GET_CONTENT");
317             intent.setType("image/jpeg");
318             startActivity(intent);
319             return true;
320         } else if (preference == mMediaPreferences[Constants.MEDIA_MISC_INDEX]) {
321             Context context = getActivity().getApplicationContext();
322             if (MemoryMeasurement.getInstance(context).isSizeOfMiscCategoryNonZero()) {
323                 startActivity(new Intent(context, MiscFilesHandler.class));
324             }
325             return true;
326         }
327
328         return false;
329     }
330      
331     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
332         @Override
333         public void onReceive(Context context, Intent intent) {
334             mMeasurement.invalidate();
335
336             if (!Environment.isExternalStorageEmulated()) {
337                 mMeasurement.measureExternal();
338             }
339             mMeasurement.measureInternal();
340         }
341     };
342
343     @Override
344     public Dialog onCreateDialog(int id) {
345         switch (id) {
346         case DLG_CONFIRM_UNMOUNT:
347                 return new AlertDialog.Builder(getActivity())
348                     .setTitle(R.string.dlg_confirm_unmount_title)
349                     .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
350                         public void onClick(DialogInterface dialog, int which) {
351                             doUnmount(true);
352                         }})
353                     .setNegativeButton(R.string.cancel, null)
354                     .setMessage(R.string.dlg_confirm_unmount_text)
355                     .create();
356         case DLG_ERROR_UNMOUNT:
357                 return new AlertDialog.Builder(getActivity())
358             .setTitle(R.string.dlg_error_unmount_title)
359             .setNeutralButton(R.string.dlg_ok, null)
360             .setMessage(R.string.dlg_error_unmount_text)
361             .create();
362         }
363         return null;
364     }
365
366     @Override
367     protected void showDialog(int id) {
368         super.showDialog(id);
369
370         switch (id) {
371         case DLG_CONFIRM_UNMOUNT:
372         case DLG_ERROR_UNMOUNT:
373             setOnCancelListener(this);
374             break;
375         }
376     }
377
378     private void doUnmount(boolean force) {
379         // Present a toast here
380         Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
381         IMountService mountService = getMountService();
382         String extStoragePath = Environment.getExternalStorageDirectory().toString();
383         try {
384             mSdMountToggle.setEnabled(false);
385             mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title));
386             mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary));
387             mountService.unmountVolume(extStoragePath, force);
388         } catch (RemoteException e) {
389             // Informative dialog to user that
390             // unmount failed.
391             showDialogInner(DLG_ERROR_UNMOUNT);
392         }
393     }
394
395     private void showDialogInner(int id) {
396         removeDialog(id);
397         showDialog(id);
398     }
399
400     private boolean hasAppsAccessingStorage() throws RemoteException {
401         String extStoragePath = Environment.getExternalStorageDirectory().toString();
402         IMountService mountService = getMountService();
403         int stUsers[] = mountService.getStorageUsers(extStoragePath);
404         if (stUsers != null && stUsers.length > 0) {
405             return true;
406         }
407         ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
408         List<ApplicationInfo> list = am.getRunningExternalApplications();
409         if (list != null && list.size() > 0) {
410             return true;
411         }
412         return false;
413     }
414
415     private void unmount() {
416         // Check if external media is in use.
417         try {
418            if (hasAppsAccessingStorage()) {
419                // Present dialog to user
420                showDialogInner(DLG_CONFIRM_UNMOUNT);
421            } else {
422                doUnmount(true);
423            }
424         } catch (RemoteException e) {
425             // Very unlikely. But present an error dialog anyway
426             Log.e(TAG, "Is MountService running?");
427             showDialogInner(DLG_ERROR_UNMOUNT);
428         }
429     }
430
431     private void mount() {
432         IMountService mountService = getMountService();
433         try {
434             if (mountService != null) {
435                 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
436             } else {
437                 Log.e(TAG, "Mount service is null, can't mount");
438             }
439         } catch (RemoteException ex) {
440         }
441     }
442
443     private void updateUiExact(long totalSize, long availSize, long appsSize, long[] mediaSizes) {
444         // There are other things that can take up storage, but we didn't measure it.
445         // add that unaccounted-for-usage to Apps Usage
446         long appsPlusRemaining = totalSize - availSize - mediaSizes[Constants.DOWNLOADS_INDEX] -
447                 mediaSizes[Constants.PIC_VIDEO_INDEX] - mediaSizes[Constants.MUSIC_INDEX] -
448                 mediaSizes[Constants.MEDIA_MISC_INDEX];
449         mInternalSize.setSummary(formatSize(totalSize));
450         mInternalAvail.setSummary(formatSize(availSize));
451         mInternalAppsUsage.setSummary(formatSize(appsPlusRemaining));
452
453         mInternalUsageChart.clear();
454         mInternalUsageChart.addEntry(appsPlusRemaining / (float) totalSize, mInternalAppsColor);
455
456         for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) {
457             if (Constants.mMediaDirs.get(i).mPreferenceName == null) {
458                 continue;
459             }
460             this.mMediaPreferences[i].setSummary(formatSize(mediaSizes[i]));
461             // don't add entry to color chart for media usage and for zero-sized dirs
462             if (i != Constants.MEDIA_INDEX && mediaSizes[i] > 0) {
463                 int color = 0;
464                 switch (i) {
465                     case Constants.DOWNLOADS_INDEX:
466                         color = mRes.getColor(R.color.memory_downloads);
467                         break;
468                     case Constants.PIC_VIDEO_INDEX:
469                         color = mRes.getColor(R.color.memory_video);
470                         break;
471                     case Constants.MUSIC_INDEX:
472                         color = mRes.getColor(R.color.memory_audio);
473                         break;
474                     case Constants.MEDIA_MISC_INDEX:
475                         color = mRes.getColor(R.color.memory_misc);
476                         break;
477                 }
478                 mInternalUsageChart.addEntry(mediaSizes[i] / (float) totalSize, color);
479             }
480         }
481         mInternalUsageChart.addEntry(availSize / (float) totalSize, mInternalAvailColor);
482         mInternalUsageChart.commit();
483     }
484
485     private void updateUiApproximate(long totalSize, long availSize) {
486         mInternalSize.setSummary(formatSize(totalSize));
487         mInternalAvail.setSummary(formatSize(availSize));
488
489         final long usedSize = totalSize - availSize;
490
491         mInternalUsageChart.clear();
492         mInternalUsageChart.addEntry(usedSize / (float) totalSize, mInternalUsedColor);
493         mInternalUsageChart.commit();
494     }
495
496     private void updateExternalStorage(long totalSize, long availSize) {
497         String status = Environment.getExternalStorageState();
498         String readOnly = "";
499         if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
500             status = Environment.MEDIA_MOUNTED;
501             readOnly = mRes.getString(R.string.read_only);
502         }
503
504         if (status.equals(Environment.MEDIA_MOUNTED)) {
505             if (!Environment.isExternalStorageRemovable()) {
506                 // This device has built-in storage that is not removable.
507                 // There is no reason for the user to unmount it.
508                 if (mSdMountToggleAdded) {
509                     mSdMountPreferenceGroup.removePreference(mSdMountToggle);
510                     mSdMountToggleAdded = false;
511                 }
512             }
513             try {
514                 mSdSize.setSummary(formatSize(totalSize));
515                 mSdAvail.setSummary(formatSize(availSize) + readOnly);
516
517                 mSdMountToggle.setEnabled(true);
518                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
519                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
520
521             } catch (IllegalArgumentException e) {
522                 // this can occur if the SD card is removed, but we haven't
523                 // received the
524                 // ACTION_MEDIA_REMOVED Intent yet.
525                 status = Environment.MEDIA_REMOVED;
526             }
527         } else {
528             mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
529             mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
530
531             if (!Environment.isExternalStorageRemovable()) {
532                 if (status.equals(Environment.MEDIA_UNMOUNTED)) {
533                     if (!mSdMountToggleAdded) {
534                         mSdMountPreferenceGroup.addPreference(mSdMountToggle);
535                         mSdMountToggleAdded = true;
536                     }
537                 }
538             }
539
540             if (status.equals(Environment.MEDIA_UNMOUNTED) || status.equals(Environment.MEDIA_NOFS)
541                     || status.equals(Environment.MEDIA_UNMOUNTABLE)) {
542                 mSdMountToggle.setEnabled(true);
543                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
544                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
545             } else {
546                 mSdMountToggle.setEnabled(false);
547                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
548                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
549             }
550         }
551     }
552
553     private String formatSize(long size) {
554         return Formatter.formatFileSize(getActivity(), size);
555     }
556
557     public void onCancel(DialogInterface dialog) {
558         // TODO: Is this really required?
559         // finish();
560     }
561
562     @Override
563     public void updateApproximateExternal(Bundle bundle) {
564         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXTERNAL_APPROXIMATE);
565         message.setData(bundle);
566         mUpdateHandler.sendMessage(message);
567     }
568
569     @Override
570     public void updateApproximateInternal(Bundle bundle) {
571         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_APPROXIMATE);
572         message.setData(bundle);
573         mUpdateHandler.sendMessage(message);
574     }
575
576     @Override
577     public void updateExactInternal(Bundle bundle) {
578         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_EXACT);
579         message.setData(bundle);
580         mUpdateHandler.sendMessage(message);
581     }
582 }