blob: 7238ed4d5a4338ebc9c2c93a3fd61878fc33f63b [file] [log] [blame]
package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.M;
import android.os.Environment;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.util.ReflectionHelpers;
@Implements(Environment.class)
public class ShadowEnvironment {
private static String externalStorageState = Environment.MEDIA_REMOVED;
private static final Map<File, Boolean> STORAGE_EMULATED = new HashMap<>();
private static final Map<File, Boolean> STORAGE_REMOVABLE = new HashMap<>();
private static boolean sIsExternalStorageEmulated;
private static Path tmpExternalFilesDirBase;
private static final List<File> externalDirs = new ArrayList<>();
private static Map<Path, String> storageState = new HashMap<>();
static Path EXTERNAL_CACHE_DIR;
static Path EXTERNAL_FILES_DIR;
@Implementation
public static String getExternalStorageState() {
return externalStorageState;
}
/**
* Sets the return value of {@link #getExternalStorageState()}.
*
* @param externalStorageState Value to return from {@link #getExternalStorageState()}.
*/
public static void setExternalStorageState(String externalStorageState) {
ShadowEnvironment.externalStorageState = externalStorageState;
}
/**
* Sets the return value of {@link #isExternalStorageEmulated()}.
*
* @param emulated Value to return from {@link #isExternalStorageEmulated()}.
*/
public static void setIsExternalStorageEmulated(boolean emulated) {
ShadowEnvironment.sIsExternalStorageEmulated = emulated;
}
@Implementation
public static File getExternalStorageDirectory() {
if (!exists(EXTERNAL_CACHE_DIR)) EXTERNAL_CACHE_DIR = RuntimeEnvironment.getTempDirectory().create("external-cache");
return EXTERNAL_CACHE_DIR.toFile();
}
@Implementation
public static File getExternalStoragePublicDirectory(String type) {
if (!exists(EXTERNAL_FILES_DIR)) EXTERNAL_FILES_DIR = RuntimeEnvironment.getTempDirectory().create("external-files");
if (type == null) return EXTERNAL_FILES_DIR.toFile();
Path path = EXTERNAL_FILES_DIR.resolve(type);
try {
Files.createDirectories(path);
} catch (IOException e) {
throw new RuntimeException(e);
}
return path.toFile();
}
@Resetter
public static void reset() {
EXTERNAL_CACHE_DIR = null;
EXTERNAL_FILES_DIR = null;
STORAGE_EMULATED.clear();
STORAGE_REMOVABLE.clear();
storageState = new HashMap<>();
externalDirs.clear();
sIsExternalStorageEmulated = false;
}
private static boolean exists(Path path) {
return path != null && Files.exists(path);
}
@Implementation
public static boolean isExternalStorageRemovable() {
final Boolean exists = STORAGE_REMOVABLE.get(getExternalStorageDirectory());
return exists != null ? exists : false;
}
@Implementation(minSdk = KITKAT)
public static String getStorageState(File directory) {
Path directoryPath = directory.toPath();
for (Map.Entry<Path, String> entry : storageState.entrySet()) {
if (directoryPath.startsWith(entry.getKey())) {
return entry.getValue();
}
}
return null;
}
@Implementation(minSdk = LOLLIPOP)
public static String getExternalStorageState(File directory) {
Path directoryPath = directory.toPath();
for (Map.Entry<Path, String> entry : storageState.entrySet()) {
if (directoryPath.startsWith(entry.getKey())) {
return entry.getValue();
}
}
return null;
}
@Implementation(minSdk = LOLLIPOP)
public static boolean isExternalStorageRemovable(File path) {
final Boolean exists = STORAGE_REMOVABLE.get(path);
return exists != null ? exists : false;
}
@Implementation(minSdk = LOLLIPOP)
public static boolean isExternalStorageEmulated(File path) {
final Boolean emulated = STORAGE_EMULATED.get(path);
return emulated != null ? emulated : false;
}
@Implementation
public static boolean isExternalStorageEmulated() {
return sIsExternalStorageEmulated;
}
/**
* Sets the "isRemovable" flag of a particular file.
*
* @param file Target file.
* @param isRemovable True if the filesystem is removable.
*/
public static void setExternalStorageRemovable(File file, boolean isRemovable) {
STORAGE_REMOVABLE.put(file, isRemovable);
}
/**
* Sets the "isEmulated" flag of a particular file.
*
* @param file Target file.
* @param isEmulated True if the filesystem is emulated.
*/
public static void setExternalStorageEmulated(File file, boolean isEmulated) {
STORAGE_EMULATED.put(file, isEmulated);
}
/**
* Adds a directory to list returned by {@link ShadowUserEnvironment#getExternalDirs()}.
*
* @param path the external dir to add
*/
public static File addExternalDir(String path) {
Path externalFileDir;
if (path == null) {
externalFileDir = null;
} else {
try {
if (tmpExternalFilesDirBase == null) {
tmpExternalFilesDirBase = RuntimeEnvironment.getTempDirectory().create("external-files-base");
}
externalFileDir = tmpExternalFilesDirBase.resolve(path);
Files.createDirectories(externalFileDir);
externalDirs.add(externalFileDir.toFile());
} catch (IOException e) {
throw new RuntimeException("Could not create external files dir", e);
}
}
if (RuntimeEnvironment.getApiLevel() >= JELLY_BEAN_MR1
&& RuntimeEnvironment.getApiLevel() < KITKAT) {
if (externalDirs.size() == 1 && externalFileDir != null) {
Environment.UserEnvironment userEnvironment =
ReflectionHelpers.getStaticField(Environment.class, "sCurrentUser");
ReflectionHelpers.setField(
userEnvironment, "mExternalStorageAndroidData", externalFileDir.toFile());
}
} else if (RuntimeEnvironment.getApiLevel() >= KITKAT && RuntimeEnvironment.getApiLevel() < M) {
Environment.UserEnvironment userEnvironment =
ReflectionHelpers.getStaticField(Environment.class, "sCurrentUser");
ReflectionHelpers.setField(userEnvironment, "mExternalDirsForApp",
externalDirs.toArray(new File[externalDirs.size()]));
}
if (externalFileDir == null) {
return null;
}
return externalFileDir.toFile();
}
/**
* Sets the {@link #getExternalStorageState(File)} for given directory.
*
* @param externalStorageState Value to return from {@link #getExternalStorageState(File)}.
*/
public static void setExternalStorageState(File directory, String state) {
storageState.put(directory.toPath(), state);
}
/**
* Shadow for {@link android.os.Environment.UserEnvironment}
*/
@Implements(className = "android.os.Environment$UserEnvironment", isInAndroidSdk = false)
public static class ShadowUserEnvironment {
@Implementation
public File[] getExternalDirs() {
return externalDirs.toArray(new File[externalDirs.size()]);
}
}
}