blob: 99126e2f3265631ac85d8bbe6ced34c04145db31 [file] [log] [blame]
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;
}
}