diff options
Diffstat (limited to 'v1/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLooper.java')
-rw-r--r-- | v1/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLooper.java | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/v1/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLooper.java b/v1/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLooper.java new file mode 100644 index 000000000..99126e2f3 --- /dev/null +++ b/v1/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLooper.java @@ -0,0 +1,193 @@ +package com.xtremelabs.robolectric.shadows; + +import android.os.Looper; +import com.xtremelabs.robolectric.Robolectric; +import com.xtremelabs.robolectric.internal.Implementation; +import com.xtremelabs.robolectric.internal.Implements; +import com.xtremelabs.robolectric.util.Scheduler; + +import static com.xtremelabs.robolectric.Robolectric.shadowOf; + +/** + * Shadow for {@code Looper} that enqueues posted {@link Runnable}s to be run (on this thread) later. {@code Runnable}s + * that are scheduled to run immediately can be triggered by calling {@link #idle()} + * todo: provide better support for advancing the clock and running queued tasks + */ + +@SuppressWarnings({"UnusedDeclaration"}) +@Implements(Looper.class) +public class ShadowLooper { + private static ThreadLocal<Looper> looperForThread = makeThreadLocalLoopers(); + private Scheduler scheduler = new Scheduler(); + private Thread myThread = Thread.currentThread(); + + boolean quit; + + private static synchronized ThreadLocal<Looper> makeThreadLocalLoopers() { + return new ThreadLocal<Looper>() { + @Override + protected Looper initialValue() { + return Robolectric.Reflection.newInstanceOf(Looper.class); + } + }; + } + + public static void resetThreadLoopers() { + looperForThread = makeThreadLocalLoopers(); + } + + @Implementation + public static Looper getMainLooper() { + return Robolectric.getShadowApplication().getMainLooper(); + } + + @Implementation + public static void loop() { + final ShadowLooper looper = shadowOf(myLooper()); + if (looper != shadowOf(getMainLooper())) { + while (!looper.quit) { + try { + synchronized (looper) { + looper.wait(); + } + } catch (InterruptedException ignore) { + } + } + } + } + + @Implementation + public static synchronized Looper myLooper() { + return looperForThread.get(); + } + + @Implementation + public void quit() { + if (this == shadowOf(getMainLooper())) throw new RuntimeException("Main thread not allowed to quit"); + synchronized (this) { + quit = true; + scheduler.reset(); + notify(); + } + } + + @Implementation + public Thread getThread() { + return myThread; + } + + public boolean hasQuit() { + return quit; + } + + public static void pauseLooper(Looper looper) { + shadowOf(looper).pause(); + } + + public static void unPauseLooper(Looper looper) { + shadowOf(looper).unPause(); + } + + public static void pauseMainLooper() { + pauseLooper(Looper.getMainLooper()); + } + + public static void unPauseMainLooper() { + unPauseLooper(Looper.getMainLooper()); + } + + public static void idleMainLooper(long interval) { + shadowOf(Looper.getMainLooper()).idle(interval); + } + + /** + * Causes {@link Runnable}s that have been scheduled to run immediately to actually run. Does not advance the + * scheduler's clock; + */ + public void idle() { + scheduler.advanceBy(0); + } + + /** + * Causes {@link Runnable}s that have been scheduled to run within the next {@code intervalMillis} milliseconds to + * run while advancing the scheduler's clock. + * + * @param intervalMillis milliseconds to advance + */ + public void idle(long intervalMillis) { + scheduler.advanceBy(intervalMillis); + } + + /** + * Causes all of the {@link Runnable}s that have been scheduled to run while advancing the scheduler's clock to the + * start time of the last scheduled {@link Runnable}. + */ + public void runToEndOfTasks() { + scheduler.advanceToLastPostedRunnable(); + } + + /** + * Causes the next {@link Runnable}(s) that have been scheduled to run while advancing the scheduler's clock to its + * start time. If more than one {@link Runnable} is scheduled to run at this time then they will all be run. + */ + public void runToNextTask() { + scheduler.advanceToNextPostedRunnable(); + } + + /** + * Causes only one of the next {@link Runnable}s that have been scheduled to run while advancing the scheduler's + * clock to its start time. Only one {@link Runnable} will run even if more than one has ben scheduled to run at the + * same time. + */ + public void runOneTask() { + scheduler.runOneTask(); + } + + /** + * Enqueue a task to be run later. + * + * @param runnable the task to be run + * @param delayMillis how many milliseconds into the (virtual) future to run it + */ + public boolean post(Runnable runnable, long delayMillis) { + if (!quit) { + scheduler.postDelayed(runnable, delayMillis); + return true; + } else { + return false; + } + } + + public boolean postAtFrontOfQueue(Runnable runnable) { + if (!quit) { + scheduler.postAtFrontOfQueue(runnable); + return true; + } else { + return false; + } + } + + public void pause() { + scheduler.pause(); + } + + public void unPause() { + scheduler.unPause(); + } + + /** + * Causes all enqueued tasks to be discarded + */ + public void reset() { + scheduler.reset(); + } + + /** + * Returns the {@link com.xtremelabs.robolectric.util.Scheduler} that is being used to manage the enqueued tasks. + * + * @return the {@link com.xtremelabs.robolectric.util.Scheduler} that is being used to manage the enqueued tasks. + */ + public Scheduler getScheduler() { + return scheduler; + } +} |