Switchable Storage Locations in an Android App
Posted by Admin • Saturday, October 13. 2012 • Category: Android
This is also a typical pattern. In my app, I need to offer the user a choice of storage locations without getting too low-level. I am content with offering three options in a ListPreference:
ImageSaveLocation saveLocation = MyUtils.ImageSaveLocation.safeValueOf(_defaultSharedPreferences.getString(getString(R.string.pref_image_save_path), MyUtils.ImageSaveLocation.Private.name()), ImageSaveLocation.Private);
File directory = saveLocation.getLocation(this); //this is the context
protected void onStart() {
super.onStart();
//good time to populate the list preference with the enum values:
ListPreference storageOptions = (ListPreference)findPreference(getString(R.string.pref_image_save_path));
String[] options = MyUtils.enumToStringArray(MyUtils.ImageSaveLocation.values());
storageOptions.setEntries(options);
storageOptions.setEntryValues(options);
}
- Private to application
- Public but on the main storage
- Public but on the removable storage (actual SD card)
Reusable utility class including the ImageSaveLocation enum
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.content.Context;
import android.util.Log;
import android.os.Environment;
public class MyUtils {
private static File s_removableStoragePath;
/**
Utility function that attempts to find the removable storage directory
(as opposed to {@link Environment#getExternalStorageDirectory()} which is usually non-removable)
by running "mount" and parsing the output. This implementation looks for
the strings "vfat" and "vold", eg:
/dev/block/vold/179:97 /mnt/extSdCard vfat rw,dirsync,nosuid,nodev,noexec,noatime,nodiratime,uid=1000,gid=1023,fmask=0002,dmask=0002,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
The result will be cached indefinitely.
@return either a valid file or null. This may not be accessible, but it does exist
*/
public static File findRemovableStorage() {
if (s_removableStoragePath != null && s_removableStoragePath.exists()) {
return s_removableStoragePath;
}
s_removableStoragePath = null;
String result = null;
try {
Process process = new ProcessBuilder().command("mount")
.redirectErrorStream(true).start();
process.waitFor();
InputStream is = process.getInputStream();
BufferedReader isr = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = isr.readLine()) != null) {
if (-1 != line.indexOf("vold") && -1 != line.indexOf("vfat")) {
String[] blocks = line.split("\\s");
if (blocks.length > 2) {
result = blocks[1];
}
}
}
if (result != null) {
s_removableStoragePath = new File(result);
if (!s_removableStoragePath.exists()) {
s_removableStoragePath = null;
}
}
} catch (Throwable t) {
Log.e("myutils",
"Unable to find external mount point");
}
return s_removableStoragePath;
}
interface ImageSaveLocationLookup {
File getFileLocation(Context context);
}
public enum ImageSaveLocation {
Private(new ImageSaveLocationLookup() {
public File getFileLocation(Context context) {
return context.getFilesDir();
}
}, R.string.description_file_save_path_private),
Public(new ImageSaveLocationLookup() {
public File getFileLocation(Context context) {
return context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
}
}, R.string.description_file_save_path_private),
Removable(new ImageSaveLocationLookup() {
public File getFileLocation(Context context) {
return findRemovableStorage();
}
}, R.string.description_file_save_path_private);
private ImageSaveLocationLookup _lookup;
private int _descriptionResource;
private ImageSaveLocation(ImageSaveLocationLookup lookup, int descriptionResource) {
_lookup = lookup;
_descriptionResource = descriptionResource;
}
/**
Get the file location for this option
@param context
@return possibly null if this location cannot be used on this device. The returned
directory might also be read-only
*/
public File getLocation(Context context) {
return _lookup.getFileLocation(context);
}
/**
For UI elements that may offer a description for each enum value
@return
*/
public int getDescriptionResource() {
return _descriptionResource;
}
/**
Safer alternative to {@link #valueOf(String)}.
If value is not one of the enum constants, returns defaultValue
@return never null
*/
public static ImageSaveLocation safeValueOf(String value, ImageSaveLocation defaultValue) {
ImageSaveLocation result = defaultValue;
try {
result = valueOf(value);
} catch (Throwable t) {}
return result;
}
}
}
Here is how this is actually used
From the activity/service:
ImageSaveLocation saveLocation = MyUtils.ImageSaveLocation.safeValueOf(_defaultSharedPreferences.getString(getString(R.string.pref_image_save_path), MyUtils.ImageSaveLocation.Private.name()), ImageSaveLocation.Private);
File directory = saveLocation.getLocation(this); //this is the context
In the Prefence Activity:
protected void onStart() {
super.onStart();
//good time to populate the list preference with the enum values:
ListPreference storageOptions = (ListPreference)findPreference(getString(R.string.pref_image_save_path));
String[] options = MyUtils.enumToStringArray(MyUtils.ImageSaveLocation.values());
storageOptions.setEntries(options);
storageOptions.setEntryValues(options);
}
0 Comments
Add Comment