All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowEnvironment Maven / Gradle / Ivy

The newest version!
package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.os.Environment;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
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;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;
import org.robolectric.util.reflector.Static;

@Implements(Environment.class)
@SuppressWarnings("NewApi")
public class ShadowEnvironment {
  private static String externalStorageState = Environment.MEDIA_REMOVED;
  private static final Map STORAGE_EMULATED = new HashMap<>();
  private static final Map STORAGE_REMOVABLE = new HashMap<>();
  private static boolean sIsExternalStorageEmulated;
  private static boolean isExternalStorageLegacy;
  private static Path tmpExternalFilesDirBase;
  private static final List externalDirs = new ArrayList<>();
  private static Map storageState = new HashMap<>();
  private static Path rootStorageDirectory;

  static Path EXTERNAL_CACHE_DIR;
  static Path EXTERNAL_FILES_DIR;

  @Implementation
  protected 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;
  }

  /**
   * Sets the return value of {@link #isExternalStorageLegacy()} ()}.
   *
   * @param legacy Value to return from {@link #isExternalStorageLegacy()}.
   */
  public static void setIsExternalStorageLegacy(boolean legacy) {
    ShadowEnvironment.isExternalStorageLegacy = legacy;
  }

  /**
   * Sets the return value of {@link #getStorageDirectory()}. This can be used for example, when
   * testing code paths that need to perform regex matching on this directory.
   *
   * 

Note that the default value provides a directory that is usable in the test environment. If * the test app uses this method to override that default directory, please clean up any files * written to that directory, as the Robolectric environment will not purge that directory when * the test ends. * * @param directory Path to return from {@link #getStorageDirectory()}. */ public static void setStorageDirectory(Path directory) { rootStorageDirectory = directory; } @Implementation(minSdk = R) protected static File getStorageDirectory() { if (rootStorageDirectory == null) { return reflector(EnvironmentReflector.class).getStorageDirectory(); } return rootStorageDirectory.toFile(); } /** * Sets the return value of {@link #getExternalStorageDirectory()}. Note that the default value * provides a directory that is usable in the test environment. If the test app uses this method * to override that default directory, please clean up any files written to that directory, as the * Robolectric environment will not purge that directory when the test ends. * * @param directory Path to return from {@link #getExternalStorageDirectory()}. */ public static void setExternalStorageDirectory(Path directory) { EXTERNAL_CACHE_DIR = directory; } @Implementation protected static File getExternalStorageDirectory() { if (EXTERNAL_CACHE_DIR == null) { EXTERNAL_CACHE_DIR = RuntimeEnvironment.getTempDirectory().createIfNotExists("external-cache"); } return EXTERNAL_CACHE_DIR.toFile(); } @Implementation protected static File[] buildExternalStorageAppCacheDirs(String packageName) { Path externalStorageDirectoryPath = getExternalStorageDirectory().toPath(); // Add cache directory in path. String cacheDirectory = packageName + "-cache"; Path path = externalStorageDirectoryPath.resolve(cacheDirectory); try { Files.createDirectory(path); } catch (FileAlreadyExistsException e) { // That's ok return new File[] {path.toFile()}; } catch (IOException e) { throw new RuntimeException(e); } return new File[] {path.toFile()}; } /** * Sets the return value of {@link #getExternalStoragePublicDirectory}. Note that the default * value provides a directory that is usable in the test environment. If the test app uses this * method to override that default directory, please clean up any files written to that directory, * as the Robolectric environment will not purge that directory when the test ends. * * @param directory Path to return from {@link #getExternalStoragePublicDirectory}. */ public static void setExternalStoragePublicDirectory(Path directory) { EXTERNAL_FILES_DIR = directory; } @Implementation protected static File getExternalStoragePublicDirectory(String type) { if (externalStorageState.equals(Environment.MEDIA_UNKNOWN)) { return null; } if (EXTERNAL_FILES_DIR == null) { EXTERNAL_FILES_DIR = RuntimeEnvironment.getTempDirectory().createIfNotExists("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() { rootStorageDirectory = null; EXTERNAL_CACHE_DIR = null; EXTERNAL_FILES_DIR = null; STORAGE_EMULATED.clear(); STORAGE_REMOVABLE.clear(); storageState = new HashMap<>(); externalDirs.clear(); externalStorageState = Environment.MEDIA_REMOVED; sIsExternalStorageEmulated = false; isExternalStorageLegacy = false; } @Implementation protected static boolean isExternalStorageRemovable() { final Boolean exists = STORAGE_REMOVABLE.get(getExternalStorageDirectory()); return exists != null ? exists : false; } @Implementation protected static String getStorageState(File directory) { Path directoryPath = directory.toPath(); for (Map.Entry entry : storageState.entrySet()) { if (directoryPath.startsWith(entry.getKey())) { return entry.getValue(); } } return null; } @Implementation protected static String getExternalStorageState(File directory) { Path directoryPath = directory.toPath(); for (Map.Entry entry : storageState.entrySet()) { if (directoryPath.startsWith(entry.getKey())) { return entry.getValue(); } } return null; } @Implementation protected static boolean isExternalStorageRemovable(File path) { final Boolean exists = STORAGE_REMOVABLE.get(path); return exists != null ? exists : false; } @Implementation protected static boolean isExternalStorageEmulated(File path) { final Boolean emulated = STORAGE_EMULATED.get(path); return emulated != null ? emulated : false; } @Implementation protected static boolean isExternalStorageEmulated() { return sIsExternalStorageEmulated; } @Implementation(minSdk = Q) protected static boolean isExternalStorageLegacy(File path) { return isExternalStorageLegacy; } @Implementation(minSdk = Q) protected static boolean isExternalStorageLegacy() { return isExternalStorageLegacy; } /** * 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() < M) { Environment.UserEnvironment userEnvironment = ReflectionHelpers.getStaticField(Environment.class, "sCurrentUser"); reflector(_UserEnvironment_.class, userEnvironment) .setExternalDirsForApp(externalDirs.toArray(new File[0])); } 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); } @Implements(className = "android.os.Environment$UserEnvironment", isInAndroidSdk = false) public static class ShadowUserEnvironment { @Implementation(minSdk = M) protected File[] getExternalDirs() { return externalDirs.toArray(new File[externalDirs.size()]); } } /** Accessor interface for Environment.UserEnvironment's internals. */ @ForType(className = "android.os.Environment$UserEnvironment") interface _UserEnvironment_ { @Accessor("mExternalDirsForApp") void setExternalDirsForApp(File[] files); @Accessor("mExternalStorageAndroidData") void setExternalStorageAndroidData(File file); } @ForType(Environment.class) interface EnvironmentReflector { @Static @Direct File getStorageDirectory(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy