2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.deviceinfo;
19 import com.android.settings.R;
20 import com.android.settings.SettingsPreferenceFragment;
22 import android.app.ActivityManager;
23 import android.app.AlertDialog;
24 import android.app.Dialog;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.DialogInterface.OnCancelListener;
31 import android.content.pm.ApplicationInfo;
32 import android.content.res.Resources;
33 import android.os.Bundle;
34 import android.hardware.Usb;
35 import android.os.Environment;
36 import android.os.IBinder;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.os.StatFs;
40 import android.os.storage.IMountService;
41 import android.os.storage.StorageEventListener;
42 import android.os.storage.StorageManager;
43 import android.preference.CheckBoxPreference;
44 import android.preference.Preference;
45 import android.preference.PreferenceScreen;
46 import android.provider.Settings;
47 import android.text.format.Formatter;
48 import android.util.Log;
49 import android.widget.Toast;
52 import java.util.List;
54 public class Memory extends SettingsPreferenceFragment implements OnCancelListener {
55 private static final String TAG = "Memory";
56 private static final boolean localLOGV = false;
58 private static final String MEMORY_SD_SIZE = "memory_sd_size";
60 private static final String MEMORY_SD_AVAIL = "memory_sd_avail";
62 private static final String MEMORY_SD_MOUNT_TOGGLE = "memory_sd_mount_toggle";
64 private static final String MEMORY_SD_FORMAT = "memory_sd_format";
66 private static final String PTP_MODE_TOGGLE = "ptp_mode_toggle";
68 private static final int DLG_CONFIRM_UNMOUNT = 1;
69 private static final int DLG_ERROR_UNMOUNT = 2;
71 private Resources mRes;
73 private Preference mSdSize;
74 private Preference mSdAvail;
75 private Preference mSdMountToggle;
76 private Preference mSdFormat;
77 private CheckBoxPreference mPtpModeToggle;
79 // Access using getMountService()
80 private IMountService mMountService = null;
82 private StorageManager mStorageManager = null;
85 public void onCreate(Bundle icicle) {
86 super.onCreate(icicle);
88 if (mStorageManager == null) {
89 mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
90 mStorageManager.registerListener(mStorageListener);
93 addPreferencesFromResource(R.xml.device_info_memory);
95 mRes = getResources();
96 mSdSize = findPreference(MEMORY_SD_SIZE);
97 mSdAvail = findPreference(MEMORY_SD_AVAIL);
98 mSdMountToggle = findPreference(MEMORY_SD_MOUNT_TOGGLE);
99 mSdFormat = findPreference(MEMORY_SD_FORMAT);
101 mPtpModeToggle = (CheckBoxPreference)findPreference(PTP_MODE_TOGGLE);
102 if (Usb.isFunctionSupported(Usb.USB_FUNCTION_MTP)) {
103 mPtpModeToggle.setChecked(Settings.System.getInt(
104 getContentResolver(),
105 Settings.System.USE_PTP_INTERFACE, 0) != 0);
107 // hide the PTP mode toggle checkbox if MTP is not supported
108 getPreferenceScreen().removePreference(mPtpModeToggle);
113 public void onResume() {
116 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
117 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
118 intentFilter.addDataScheme("file");
119 getActivity().registerReceiver(mReceiver, intentFilter);
121 updateMemoryStatus();
124 StorageEventListener mStorageListener = new StorageEventListener() {
127 public void onStorageStateChanged(String path, String oldState, String newState) {
128 Log.i(TAG, "Received storage state changed notification that " +
129 path + " changed state from " + oldState +
131 updateMemoryStatus();
136 public void onPause() {
138 getActivity().unregisterReceiver(mReceiver);
142 public void onDestroy() {
143 if (mStorageManager != null && mStorageListener != null) {
144 mStorageManager.unregisterListener(mStorageListener);
149 private synchronized IMountService getMountService() {
150 if (mMountService == null) {
151 IBinder service = ServiceManager.getService("mount");
152 if (service != null) {
153 mMountService = IMountService.Stub.asInterface(service);
155 Log.e(TAG, "Can't get mount service");
158 return mMountService;
162 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
163 if (preference == mSdMountToggle) {
164 String status = Environment.getExternalStorageState();
165 if (status.equals(Environment.MEDIA_MOUNTED)) {
171 } else if (preference == mSdFormat) {
172 Intent intent = new Intent(Intent.ACTION_VIEW);
173 intent.setClass(getActivity(), com.android.settings.MediaFormat.class);
174 startActivity(intent);
176 } else if (preference == mPtpModeToggle) {
177 Settings.System.putInt(getContentResolver(),
178 Settings.System.USE_PTP_INTERFACE,
179 mPtpModeToggle.isChecked() ? 1 : 0);
186 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
188 public void onReceive(Context context, Intent intent) {
189 updateMemoryStatus();
194 public Dialog onCreateDialog(int id) {
196 case DLG_CONFIRM_UNMOUNT:
197 return new AlertDialog.Builder(getActivity())
198 .setTitle(R.string.dlg_confirm_unmount_title)
199 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
200 public void onClick(DialogInterface dialog, int which) {
203 .setNegativeButton(R.string.cancel, null)
204 .setMessage(R.string.dlg_confirm_unmount_text)
205 .setOnCancelListener(this)
207 case DLG_ERROR_UNMOUNT:
208 return new AlertDialog.Builder(getActivity())
209 .setTitle(R.string.dlg_error_unmount_title)
210 .setNeutralButton(R.string.dlg_ok, null)
211 .setMessage(R.string.dlg_error_unmount_text)
212 .setOnCancelListener(this)
218 private void doUnmount(boolean force) {
219 // Present a toast here
220 Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
221 IMountService mountService = getMountService();
222 String extStoragePath = Environment.getExternalStorageDirectory().toString();
224 mSdMountToggle.setEnabled(false);
225 mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title));
226 mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary));
227 mountService.unmountVolume(extStoragePath, force);
228 } catch (RemoteException e) {
229 // Informative dialog to user that
231 showDialogInner(DLG_ERROR_UNMOUNT);
235 private void showDialogInner(int id) {
240 private boolean hasAppsAccessingStorage() throws RemoteException {
241 String extStoragePath = Environment.getExternalStorageDirectory().toString();
242 IMountService mountService = getMountService();
243 int stUsers[] = mountService.getStorageUsers(extStoragePath);
244 if (stUsers != null && stUsers.length > 0) {
247 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
248 List<ApplicationInfo> list = am.getRunningExternalApplications();
249 if (list != null && list.size() > 0) {
255 private void unmount() {
256 // Check if external media is in use.
258 if (hasAppsAccessingStorage()) {
259 if (localLOGV) Log.i(TAG, "Do have storage users accessing media");
260 // Present dialog to user
261 showDialogInner(DLG_CONFIRM_UNMOUNT);
265 } catch (RemoteException e) {
266 // Very unlikely. But present an error dialog anyway
267 Log.e(TAG, "Is MountService running?");
268 showDialogInner(DLG_ERROR_UNMOUNT);
272 private void mount() {
273 IMountService mountService = getMountService();
275 if (mountService != null) {
276 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
278 Log.e(TAG, "Mount service is null, can't mount");
280 } catch (RemoteException ex) {
284 private void updateMemoryStatus() {
285 String status = Environment.getExternalStorageState();
286 String readOnly = "";
287 if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
288 status = Environment.MEDIA_MOUNTED;
289 readOnly = mRes.getString(R.string.read_only);
292 mSdFormat.setEnabled(false);
294 if (status.equals(Environment.MEDIA_MOUNTED)) {
296 File path = Environment.getExternalStorageDirectory();
297 StatFs stat = new StatFs(path.getPath());
298 long blockSize = stat.getBlockSize();
299 long totalBlocks = stat.getBlockCount();
300 long availableBlocks = stat.getAvailableBlocks();
302 mSdSize.setSummary(formatSize(totalBlocks * blockSize));
303 mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly);
305 mSdMountToggle.setEnabled(true);
306 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
307 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
309 } catch (IllegalArgumentException e) {
310 // this can occur if the SD card is removed, but we haven't received the
311 // ACTION_MEDIA_REMOVED Intent yet.
312 status = Environment.MEDIA_REMOVED;
316 mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
317 mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
320 if (status.equals(Environment.MEDIA_UNMOUNTED) ||
321 status.equals(Environment.MEDIA_NOFS) ||
322 status.equals(Environment.MEDIA_UNMOUNTABLE) ) {
323 mSdFormat.setEnabled(true);
324 mSdMountToggle.setEnabled(true);
325 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
326 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
328 mSdMountToggle.setEnabled(false);
329 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
330 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
334 File path = Environment.getDataDirectory();
335 StatFs stat = new StatFs(path.getPath());
336 long blockSize = stat.getBlockSize();
337 long availableBlocks = stat.getAvailableBlocks();
338 findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks * blockSize));
341 private String formatSize(long size) {
342 return Formatter.formatFileSize(getActivity(), size);
345 public void onCancel(DialogInterface dialog) {
346 // TODO: Is this really required?