Home | History | Annotate | Download | only in shadows
      1 package com.xtremelabs.robolectric.shadows;
      2 
      3 import android.os.Looper;
      4 import com.xtremelabs.robolectric.Robolectric;
      5 import com.xtremelabs.robolectric.internal.Implementation;
      6 import com.xtremelabs.robolectric.internal.Implements;
      7 import com.xtremelabs.robolectric.util.Scheduler;
      8 
      9 import static com.xtremelabs.robolectric.Robolectric.shadowOf;
     10 
     11 /**
     12  * Shadow for {@code Looper} that enqueues posted {@link Runnable}s to be run (on this thread) later. {@code Runnable}s
     13  * that are scheduled to run immediately can be triggered by calling {@link #idle()}
     14  * todo: provide better support for advancing the clock and running queued tasks
     15  */
     16 
     17 @SuppressWarnings({"UnusedDeclaration"})
     18 @Implements(Looper.class)
     19 public class ShadowLooper {
     20     private static ThreadLocal<Looper> looperForThread = makeThreadLocalLoopers();
     21     private Scheduler scheduler = new Scheduler();
     22     private Thread myThread = Thread.currentThread();
     23 
     24     boolean quit;
     25 
     26     private static synchronized ThreadLocal<Looper> makeThreadLocalLoopers() {
     27         return new ThreadLocal<Looper>() {
     28             @Override
     29             protected Looper initialValue() {
     30                 return Robolectric.Reflection.newInstanceOf(Looper.class);
     31             }
     32         };
     33     }
     34 
     35     public static void resetThreadLoopers() {
     36         looperForThread = makeThreadLocalLoopers();
     37     }
     38 
     39     @Implementation
     40     public static Looper getMainLooper() {
     41         return Robolectric.getShadowApplication().getMainLooper();
     42     }
     43 
     44     @Implementation
     45     public static void loop() {
     46         final ShadowLooper looper = shadowOf(myLooper());
     47         if (looper != shadowOf(getMainLooper())) {
     48             while (!looper.quit) {
     49                 try {
     50                     synchronized (looper) {
     51                         looper.wait();
     52                     }
     53                 } catch (InterruptedException ignore) {
     54                 }
     55             }
     56         }
     57     }
     58 
     59     @Implementation
     60     public static synchronized Looper myLooper() {
     61         return looperForThread.get();
     62     }
     63 
     64     @Implementation
     65     public void quit() {
     66         if (this == shadowOf(getMainLooper())) throw new RuntimeException("Main thread not allowed to quit");
     67         synchronized (this) {
     68             quit = true;
     69             scheduler.reset();
     70             notify();
     71         }
     72     }
     73 
     74     @Implementation
     75     public Thread getThread() {
     76     	return myThread;
     77     }
     78 
     79     public boolean hasQuit() {
     80         return quit;
     81     }
     82 
     83     public static void pauseLooper(Looper looper) {
     84         shadowOf(looper).pause();
     85     }
     86 
     87     public static void unPauseLooper(Looper looper) {
     88         shadowOf(looper).unPause();
     89     }
     90 
     91     public static void pauseMainLooper() {
     92         pauseLooper(Looper.getMainLooper());
     93     }
     94 
     95     public static void unPauseMainLooper() {
     96         unPauseLooper(Looper.getMainLooper());
     97     }
     98 
     99     public static void idleMainLooper(long interval) {
    100         shadowOf(Looper.getMainLooper()).idle(interval);
    101     }
    102 
    103     /**
    104      * Causes {@link Runnable}s that have been scheduled to run immediately to actually run. Does not advance the
    105      * scheduler's clock;
    106      */
    107     public void idle() {
    108         scheduler.advanceBy(0);
    109     }
    110 
    111     /**
    112      * Causes {@link Runnable}s that have been scheduled to run within the next {@code intervalMillis} milliseconds to
    113      * run while advancing the scheduler's clock.
    114      *
    115      * @param intervalMillis milliseconds to advance
    116      */
    117     public void idle(long intervalMillis) {
    118         scheduler.advanceBy(intervalMillis);
    119     }
    120 
    121     /**
    122      * Causes all of the {@link Runnable}s that have been scheduled to run while advancing the scheduler's clock to the
    123      * start time of the last scheduled {@link Runnable}.
    124      */
    125     public void runToEndOfTasks() {
    126         scheduler.advanceToLastPostedRunnable();
    127     }
    128 
    129     /**
    130      * Causes the next {@link Runnable}(s) that have been scheduled to run while advancing the scheduler's clock to its
    131      * start time. If more than one {@link Runnable} is scheduled to run at this time then they will all be run.
    132      */
    133     public void runToNextTask() {
    134         scheduler.advanceToNextPostedRunnable();
    135     }
    136 
    137     /**
    138      * Causes only one of the next {@link Runnable}s that have been scheduled to run while advancing the scheduler's
    139      * clock to its start time. Only one {@link Runnable} will run even if more than one has ben scheduled to run at the
    140      * same time.
    141      */
    142     public void runOneTask() {
    143         scheduler.runOneTask();
    144     }
    145 
    146     /**
    147      * Enqueue a task to be run later.
    148      *
    149      * @param runnable    the task to be run
    150      * @param delayMillis how many milliseconds into the (virtual) future to run it
    151      */
    152     public boolean post(Runnable runnable, long delayMillis) {
    153         if (!quit) {
    154             scheduler.postDelayed(runnable, delayMillis);
    155             return true;
    156         } else {
    157             return false;
    158         }
    159     }
    160 
    161     public boolean postAtFrontOfQueue(Runnable runnable) {
    162         if (!quit) {
    163             scheduler.postAtFrontOfQueue(runnable);
    164             return true;
    165         } else {
    166             return false;
    167         }
    168     }
    169 
    170     public void pause() {
    171         scheduler.pause();
    172     }
    173 
    174     public void unPause() {
    175         scheduler.unPause();
    176     }
    177 
    178     /**
    179      * Causes all enqueued tasks to be discarded
    180      */
    181     public void reset() {
    182         scheduler.reset();
    183     }
    184 
    185     /**
    186      * Returns the {@link com.xtremelabs.robolectric.util.Scheduler} that is being used to manage the enqueued tasks.
    187      *
    188      * @return the {@link com.xtremelabs.robolectric.util.Scheduler} that is being used to manage the enqueued tasks.
    189      */
    190     public Scheduler getScheduler() {
    191         return scheduler;
    192     }
    193 }
    194