blob: f1c7fbe5e5d176989dc5f09a2b0b9c97ca6e0689 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.deviceinfo;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.storage.IMountService;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
public class Memory extends SettingsPreferenceFragment {
private static final String TAG = "MemorySettings";
private static final int DLG_CONFIRM_UNMOUNT = 1;
private static final int DLG_ERROR_UNMOUNT = 2;
private Resources mResources;
// The mountToggle Preference that has last been clicked.
// Assumes no two successive unmount event on 2 different volumes are performed before the first
// one's preference is disabled
private static Preference mLastClickedMountToggle;
private static String mClickedMountPoint;
// Access using getMountService()
private IMountService mMountService = null;
private StorageManager mStorageManager = null;
private UsbManager mUsbManager = null;
private StorageVolumePreferenceCategory mInternalStorageVolumePreferenceCategory;
private StorageVolumePreferenceCategory[] mStorageVolumePreferenceCategories;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
if (mStorageManager == null) {
mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
mStorageManager.registerListener(mStorageListener);
}
addPreferencesFromResource(R.xml.device_info_memory);
mResources = getResources();
if (!Environment.isExternalStorageEmulated()) {
// External storage is separate from internal storage; need to
// show internal storage as a separate item.
mInternalStorageVolumePreferenceCategory = new StorageVolumePreferenceCategory(
getActivity(), mResources, null, mStorageManager, false);
getPreferenceScreen().addPreference(mInternalStorageVolumePreferenceCategory);
mInternalStorageVolumePreferenceCategory.init();
}
StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
int length = storageVolumes.length;
mStorageVolumePreferenceCategories = new StorageVolumePreferenceCategory[length];
for (int i = 0; i < length; i++) {
StorageVolume storageVolume = storageVolumes[i];
boolean isPrimary = i == 0;
mStorageVolumePreferenceCategories[i] = new StorageVolumePreferenceCategory(
getActivity(), mResources, storageVolume, mStorageManager, isPrimary);
getPreferenceScreen().addPreference(mStorageVolumePreferenceCategories[i]);
mStorageVolumePreferenceCategories[i].init();
}
setHasOptionsMenu(true);
}
private boolean isMassStorageEnabled() {
// mass storage is enabled if primary volume supports it
final StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
return (storageVolumes.length > 0 && storageVolumes[0].allowMassStorage());
}
@Override
public void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
intentFilter.addDataScheme("file");
getActivity().registerReceiver(mMediaScannerReceiver, intentFilter);
intentFilter = new IntentFilter();
intentFilter.addAction(UsbManager.ACTION_USB_STATE);
getActivity().registerReceiver(mMediaScannerReceiver, intentFilter);
if (mInternalStorageVolumePreferenceCategory != null) {
mInternalStorageVolumePreferenceCategory.onResume();
}
for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
mStorageVolumePreferenceCategories[i].onResume();
}
}
StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onStorageStateChanged(String path, String oldState, String newState) {
Log.i(TAG, "Received storage state changed notification that " + path +
" changed state from " + oldState + " to " + newState);
for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i];
if (path.equals(svpc.getStorageVolume().getPath())) {
svpc.onStorageStateChanged();
break;
}
}
}
};
@Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mMediaScannerReceiver);
if (mInternalStorageVolumePreferenceCategory != null) {
mInternalStorageVolumePreferenceCategory.onPause();
}
for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
mStorageVolumePreferenceCategories[i].onPause();
}
}
@Override
public void onDestroy() {
if (mStorageManager != null && mStorageListener != null) {
mStorageManager.unregisterListener(mStorageListener);
}
super.onDestroy();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.storage, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
final MenuItem usb = menu.findItem(R.id.storage_usb);
usb.setVisible(!isMassStorageEnabled());
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.storage_usb:
if (getActivity() instanceof PreferenceActivity) {
((PreferenceActivity) getActivity()).startPreferencePanel(
UsbSettings.class.getCanonicalName(),
null,
R.string.storage_title_usb, null,
this, 0);
} else {
startFragment(this, UsbSettings.class.getCanonicalName(), -1, null);
}
return true;
}
return super.onOptionsItemSelected(item);
}
private synchronized IMountService getMountService() {
if (mMountService == null) {
IBinder service = ServiceManager.getService("mount");
if (service != null) {
mMountService = IMountService.Stub.asInterface(service);
} else {
Log.e(TAG, "Can't get mount service");
}
}
return mMountService;
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i];
Intent intent = svpc.intentForClick(preference);
if (intent != null) {
// Don't go across app boundary if monkey is running
if (!Utils.isMonkeyRunning()) {
startActivity(intent);
}
return true;
}
if (svpc.mountToggleClicked(preference)) {
mLastClickedMountToggle = preference;
final StorageVolume storageVolume = svpc.getStorageVolume();
mClickedMountPoint = storageVolume.getPath();
String state = mStorageManager.getVolumeState(storageVolume.getPath());
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
unmount();
} else {
mount();
}
return true;
}
}
return false;
}
private final BroadcastReceiver mMediaScannerReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_STATE)) {
boolean isUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
String usbFunction = mUsbManager.getDefaultFunction();
for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
mStorageVolumePreferenceCategories[i].onUsbStateChanged(isUsbConnected, usbFunction);
}
} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
// mInternalStorageVolumePreferenceCategory is not affected by the media scanner
for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
mStorageVolumePreferenceCategories[i].onMediaScannerFinished();
}
}
}
};
@Override
public Dialog onCreateDialog(int id) {
switch (id) {
case DLG_CONFIRM_UNMOUNT:
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.dlg_confirm_unmount_title)
.setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
doUnmount();
}})
.setNegativeButton(R.string.cancel, null)
.setMessage(R.string.dlg_confirm_unmount_text)
.create();
case DLG_ERROR_UNMOUNT:
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.dlg_error_unmount_title)
.setNeutralButton(R.string.dlg_ok, null)
.setMessage(R.string.dlg_error_unmount_text)
.create();
}
return null;
}
private void doUnmount() {
// Present a toast here
Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
IMountService mountService = getMountService();
try {
mLastClickedMountToggle.setEnabled(false);
mLastClickedMountToggle.setTitle(mResources.getString(R.string.sd_ejecting_title));
mLastClickedMountToggle.setSummary(mResources.getString(R.string.sd_ejecting_summary));
mountService.unmountVolume(mClickedMountPoint, true, false);
} catch (RemoteException e) {
// Informative dialog to user that unmount failed.
showDialogInner(DLG_ERROR_UNMOUNT);
}
}
private void showDialogInner(int id) {
removeDialog(id);
showDialog(id);
}
private boolean hasAppsAccessingStorage() throws RemoteException {
IMountService mountService = getMountService();
int stUsers[] = mountService.getStorageUsers(mClickedMountPoint);
if (stUsers != null && stUsers.length > 0) {
return true;
}
// TODO FIXME Parameterize with mountPoint and uncomment.
// On HC-MR2, no apps can be installed on sd and the emulated internal storage is not
// removable: application cannot interfere with unmount
/*
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List<ApplicationInfo> list = am.getRunningExternalApplications();
if (list != null && list.size() > 0) {
return true;
}
*/
// Better safe than sorry. Assume the storage is used to ask for confirmation.
return true;
}
private void unmount() {
// Check if external media is in use.
try {
if (hasAppsAccessingStorage()) {
// Present dialog to user
showDialogInner(DLG_CONFIRM_UNMOUNT);
} else {
doUnmount();
}
} catch (RemoteException e) {
// Very unlikely. But present an error dialog anyway
Log.e(TAG, "Is MountService running?");
showDialogInner(DLG_ERROR_UNMOUNT);
}
}
private void mount() {
IMountService mountService = getMountService();
try {
if (mountService != null) {
mountService.mountVolume(mClickedMountPoint);
} else {
Log.e(TAG, "Mount service is null, can't mount");
}
} catch (RemoteException ex) {
// Not much can be done
}
}
}