• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric;
2 
3 import static org.robolectric.annotation.LooperMode.Mode.LEGACY;
4 import static org.robolectric.shadows.ShadowLooper.assertLooperMode;
5 
6 import android.app.Application;
7 import android.app.ResourcesManager;
8 import android.content.Context;
9 import android.content.res.Configuration;
10 import android.content.res.Resources;
11 import android.graphics.Bitmap;
12 import android.util.DisplayMetrics;
13 import android.view.Display;
14 import com.google.common.base.Supplier;
15 import java.nio.file.Path;
16 import org.robolectric.android.Bootstrap;
17 import org.robolectric.android.ConfigurationV25;
18 import org.robolectric.res.ResourceTable;
19 import org.robolectric.shadows.ShadowDisplayManager;
20 import org.robolectric.shadows.ShadowInstrumentation;
21 import org.robolectric.shadows.ShadowView;
22 import org.robolectric.util.Scheduler;
23 import org.robolectric.util.TempDirectory;
24 
25 public class RuntimeEnvironment {
26   /**
27    * @deprecated Use {@link #getApplication} instead. Note that unlike the alternative, this field
28    *     is inherently incompatible with {@link
29    *     org.robolectric.annotation.experimental.LazyApplication}. This field may be removed in a
30    *     later release
31    */
32   @Deprecated public static Context systemContext;
33 
34   /**
35    * @deprecated Please use {#getApplication} instead. Accessing this field directly is inherently
36    *     incompatible with {@link org.robolectric.annotation.experimental.LazyApplication} and
37    *     Robolectric makes no guarantees if a test *modifies* this field during execution.
38    */
39   @Deprecated public static volatile Application application;
40 
41   private static volatile Thread mainThread;
42   private static volatile Object activityThread;
43   private static int apiLevel;
44   private static Scheduler masterScheduler;
45   private static ResourceTable systemResourceTable;
46   private static ResourceTable appResourceTable;
47   private static ResourceTable compileTimeResourceTable;
48   private static TempDirectory tempDirectory = new TempDirectory("no-test-yet");
49   private static Path androidFrameworkJar;
50   public static Path compileTimeSystemResourcesFile;
51 
52   private static Supplier<Application> applicationSupplier;
53   private static final Object supplierLock = new Object();
54 
55   /**
56    * Get a reference to the {@link Application} under test.
57    *
58    * <p>The Application may be created a test setup time or created lazily at call time, based on
59    * the test's {@link org.robolectric.annotation.experimental.LazyApplication} setting. If lazy
60    * loading is enabled, this method must be called on the main/test thread.
61    *
62    * <p>An alternate API outside of Robolectric is {@link
63    * androidx.test.core.app.ApplicationProvider#getApplicationContext()}, which is preferable if you
64    * desire cross platform tests that work on the JVM and real Android devices.
65    */
getApplication()66   public static Application getApplication() {
67     // IMPORTANT NOTE: Given the order in which these are nulled out when cleaning up in
68     // AndroidTestEnvironment, the application null check must happen before the supplier null
69     // check. Otherwise the get() call can try to load an application that has already been
70     // loaded and cleaned up (as well as race with other threads trying to load the "correct"
71     // application)
72     if (application == null) {
73       synchronized (supplierLock) {
74         if (applicationSupplier != null) {
75           ShadowInstrumentation.runOnMainSyncNoIdle(() -> application = applicationSupplier.get());
76         }
77       }
78     }
79     return application;
80   }
81 
82   /** internal use only */
setApplicationSupplier(Supplier<Application> applicationSupplier)83   public static void setApplicationSupplier(Supplier<Application> applicationSupplier) {
84     synchronized (supplierLock) {
85       RuntimeEnvironment.applicationSupplier = applicationSupplier;
86     }
87   }
88 
89   private static Class<? extends Application> applicationClass;
90 
getConfiguredApplicationClass()91   public static Class<? extends Application> getConfiguredApplicationClass() {
92     return applicationClass;
93   }
94 
setConfiguredApplicationClass(Class<? extends Application> clazz)95   public static void setConfiguredApplicationClass(Class<? extends Application> clazz) {
96     applicationClass = clazz;
97   }
98 
99   /**
100    * Tests if the given thread is currently set as the main thread.
101    *
102    * @param thread the thread to test.
103    * @return true if the specified thread is the main thread, false otherwise.
104    * @see #isMainThread()
105    */
isMainThread(Thread thread)106   public static boolean isMainThread(Thread thread) {
107     assertLooperMode(LEGACY);
108     return thread == mainThread;
109   }
110 
111   /**
112    * Tests if the current thread is currently set as the main thread.
113    *
114    * <p>Not supported in realistic looper mode.
115    *
116    * @return true if the current thread is the main thread, false otherwise.
117    */
isMainThread()118   public static boolean isMainThread() {
119     assertLooperMode(LEGACY);
120     return isMainThread(Thread.currentThread());
121   }
122 
123   /**
124    * Retrieves the main thread. The main thread is the thread to which the main looper is attached.
125    * Defaults to the thread that initialises the {@link RuntimeEnvironment} class.
126    *
127    * <p>Not supported in realistic looper mode.
128    *
129    * @return The main thread.
130    * @see #setMainThread(Thread)
131    * @see #isMainThread()
132    */
getMainThread()133   public static Thread getMainThread() {
134     assertLooperMode(LEGACY);
135     return mainThread;
136   }
137 
138   /**
139    * Sets the main thread. The main thread is the thread to which the main looper is attached.
140    * Defaults to the thread that initialises the {@link RuntimeEnvironment} class.
141    *
142    * <p>Not supported in realistic looper mode.
143    *
144    * @param newMainThread the new main thread.
145    * @see #setMainThread(Thread)
146    * @see #isMainThread()
147    */
setMainThread(Thread newMainThread)148   public static void setMainThread(Thread newMainThread) {
149     assertLooperMode(LEGACY);
150     mainThread = newMainThread;
151   }
152 
getActivityThread()153   public static Object getActivityThread() {
154     return activityThread;
155   }
156 
setActivityThread(Object newActivityThread)157   public static void setActivityThread(Object newActivityThread) {
158     activityThread = newActivityThread;
159   }
160 
161   /**
162    * Returns a qualifier string describing the current {@link Configuration} of the system
163    * resources.
164    *
165    * @return a qualifier string as described
166    *     (https://developer.android.com/guide/topics/resources/providing-resources.html#QualifierRules)[here].
167    */
getQualifiers()168   public static String getQualifiers() {
169     Resources systemResources = Resources.getSystem();
170     return getQualifiers(systemResources.getConfiguration(), systemResources.getDisplayMetrics());
171   }
172 
173   /**
174    * Returns a qualifier string describing the given configuration and display metrics.
175    *
176    * @param configuration the configuration.
177    * @param displayMetrics the display metrics.
178    * @return a qualifier string as described
179    *     (https://developer.android.com/guide/topics/resources/providing-resources.html#QualifierRules)[here].
180    */
getQualifiers(Configuration configuration, DisplayMetrics displayMetrics)181   public static String getQualifiers(Configuration configuration, DisplayMetrics displayMetrics) {
182     return ConfigurationV25.resourceQualifierString(configuration, displayMetrics);
183   }
184 
185   /**
186    * Overrides the current device configuration.
187    *
188    * <p>If {@param newQualifiers} starts with a plus ('+'), the prior configuration is used as the
189    * base configuration, with the given changes applied additively. Otherwise, default values are
190    * used for unspecified properties, as described <a
191    * href="http://robolectric.org/device-configuration/">here</a>.
192    *
193    * @param newQualifiers the qualifiers to apply
194    */
setQualifiers(String newQualifiers)195   public static void setQualifiers(String newQualifiers) {
196     ShadowDisplayManager.changeDisplay(Display.DEFAULT_DISPLAY, newQualifiers);
197 
198     Configuration configuration;
199     DisplayMetrics displayMetrics = new DisplayMetrics();
200 
201     if (newQualifiers.startsWith("+")) {
202       configuration = new Configuration(Resources.getSystem().getConfiguration());
203       displayMetrics.setTo(Resources.getSystem().getDisplayMetrics());
204     } else {
205       configuration = new Configuration();
206     }
207     Bootstrap.applyQualifiers(newQualifiers, getApiLevel(), configuration, displayMetrics);
208     if (ShadowView.useRealGraphics()) {
209       Bitmap.setDefaultDensity(displayMetrics.densityDpi);
210     }
211 
212     updateConfiguration(configuration, displayMetrics);
213   }
214 
setFontScale(float fontScale)215   public static void setFontScale(float fontScale) {
216     Resources systemResources = getApplication().getResources();
217     DisplayMetrics displayMetrics = systemResources.getDisplayMetrics();
218     Configuration configuration = systemResources.getConfiguration();
219 
220     displayMetrics.scaledDensity = displayMetrics.density * fontScale;
221     configuration.fontScale = fontScale;
222 
223     updateConfiguration(configuration, displayMetrics);
224   }
225 
getFontScale()226   public static float getFontScale() {
227     Resources systemResources = getApplication().getResources();
228     return systemResources.getConfiguration().fontScale;
229   }
230 
updateConfiguration( Configuration configuration, DisplayMetrics displayMetrics)231   private static void updateConfiguration(
232       Configuration configuration, DisplayMetrics displayMetrics) {
233     // Update the resources last so that listeners will have a consistent environment.
234     // TODO(paulsowden): Can we call ResourcesManager.getInstance().applyConfigurationToResources()?
235     if (ResourcesManager.getInstance().getConfiguration() != null) {
236       ResourcesManager.getInstance().getConfiguration().updateFrom(configuration);
237     }
238     Resources.getSystem().updateConfiguration(configuration, displayMetrics);
239     if (RuntimeEnvironment.application != null) {
240       getApplication().getResources().updateConfiguration(configuration, displayMetrics);
241     } else {
242       // if application is not yet loaded, update the configuration in Bootstrap so that the
243       // changes will be propagated once the application is finally loaded
244       Bootstrap.updateDisplayResources(configuration, displayMetrics);
245     }
246   }
247 
getApiLevel()248   public static int getApiLevel() {
249     return apiLevel;
250   }
251 
252   /**
253    * Retrieves the current master scheduler. This scheduler is always used by the main {@link
254    * android.os.Looper Looper}, and if the global scheduler option is set it is also used for the
255    * background scheduler and for all other {@link android.os.Looper Looper}s
256    *
257    * @return The current master scheduler.
258    * @see #setMasterScheduler(Scheduler) see
259    *     org.robolectric.Robolectric#getForegroundThreadScheduler() see
260    *     org.robolectric.Robolectric#getBackgroundThreadScheduler()
261    */
getMasterScheduler()262   public static Scheduler getMasterScheduler() {
263     return masterScheduler;
264   }
265 
266   /**
267    * Sets the current master scheduler. See {@link #getMasterScheduler()} for details. Note that
268    * this method is primarily intended to be called by the Robolectric core setup code. Changing the
269    * master scheduler during a test will have unpredictable results.
270    *
271    * @param masterScheduler the new master scheduler.
272    * @see #getMasterScheduler() see org.robolectric.Robolectric#getForegroundThreadScheduler() see
273    *     org.robolectric.Robolectric#getBackgroundThreadScheduler()
274    */
setMasterScheduler(Scheduler masterScheduler)275   public static void setMasterScheduler(Scheduler masterScheduler) {
276     RuntimeEnvironment.masterScheduler = masterScheduler;
277   }
278 
setSystemResourceTable(ResourceTable systemResourceTable)279   public static void setSystemResourceTable(ResourceTable systemResourceTable) {
280     RuntimeEnvironment.systemResourceTable = systemResourceTable;
281   }
282 
setAppResourceTable(ResourceTable appResourceTable)283   public static void setAppResourceTable(ResourceTable appResourceTable) {
284     RuntimeEnvironment.appResourceTable = appResourceTable;
285   }
286 
getSystemResourceTable()287   public static ResourceTable getSystemResourceTable() {
288     return systemResourceTable;
289   }
290 
getAppResourceTable()291   public static ResourceTable getAppResourceTable() {
292     return appResourceTable;
293   }
294 
setCompileTimeResourceTable(ResourceTable compileTimeResourceTable)295   public static void setCompileTimeResourceTable(ResourceTable compileTimeResourceTable) {
296     RuntimeEnvironment.compileTimeResourceTable = compileTimeResourceTable;
297   }
298 
getCompileTimeResourceTable()299   public static ResourceTable getCompileTimeResourceTable() {
300     return compileTimeResourceTable;
301   }
302 
setTempDirectory(TempDirectory tempDirectory)303   public static void setTempDirectory(TempDirectory tempDirectory) {
304     RuntimeEnvironment.tempDirectory = tempDirectory;
305   }
306 
getTempDirectory()307   public static TempDirectory getTempDirectory() {
308     return tempDirectory;
309   }
310 
setAndroidFrameworkJarPath(Path localArtifactPath)311   public static void setAndroidFrameworkJarPath(Path localArtifactPath) {
312     RuntimeEnvironment.androidFrameworkJar = localArtifactPath;
313   }
314 
getAndroidFrameworkJarPath()315   public static Path getAndroidFrameworkJarPath() {
316     return RuntimeEnvironment.androidFrameworkJar;
317   }
318 }
319