1 package com.xtremelabs.robolectric.util;
2 
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.ListIterator;
7 
8 public class Scheduler {
9     private List<PostedRunnable> postedRunnables = new ArrayList<PostedRunnable>();
10     private long currentTime = 0;
11     private boolean paused = false;
12 
getCurrentTime()13     public long getCurrentTime() {
14         return currentTime;
15     }
16 
pause()17     public void pause() {
18         paused = true;
19     }
20 
unPause()21     public void unPause() {
22         paused = false;
23         advanceToLastPostedRunnable();
24     }
25 
isPaused()26     public boolean isPaused() {
27         return paused;
28     }
29 
postDelayed(Runnable runnable, long delayMillis)30     public void postDelayed(Runnable runnable, long delayMillis) {
31         if (paused || delayMillis > 0) {
32             postedRunnables.add(new PostedRunnable(runnable, currentTime + delayMillis));
33             Collections.sort(postedRunnables);
34         } else {
35             runnable.run();
36         }
37     }
38 
post(Runnable runnable)39     public void post(Runnable runnable) {
40         postDelayed(runnable, 0);
41     }
42 
postAtFrontOfQueue(Runnable runnable)43     public void postAtFrontOfQueue(Runnable runnable) {
44         if (paused) {
45             postedRunnables.add(0, new PostedRunnable(runnable, currentTime));
46         } else {
47             runnable.run();
48         }
49     }
50 
remove(Runnable runnable)51     public void remove(Runnable runnable) {
52         ListIterator<PostedRunnable> iterator = postedRunnables.listIterator();
53         while (iterator.hasNext()) {
54             PostedRunnable next = iterator.next();
55             if (next.runnable == runnable) {
56                 iterator.remove();
57             }
58         }
59     }
60 
advanceToLastPostedRunnable()61     public boolean advanceToLastPostedRunnable() {
62         if (enqueuedTaskCount() < 1) {
63             return false;
64         }
65 
66         return advanceTo(postedRunnables.get(postedRunnables.size() - 1).scheduledTime);
67     }
68 
advanceToNextPostedRunnable()69     public boolean advanceToNextPostedRunnable() {
70         if (enqueuedTaskCount() < 1) {
71             return false;
72         }
73 
74         return advanceTo(postedRunnables.get(0).scheduledTime);
75     }
76 
advanceBy(long intervalMs)77     public boolean advanceBy(long intervalMs) {
78         long endingTime = currentTime + intervalMs;
79         return advanceTo(endingTime);
80     }
81 
advanceTo(long endingTime)82     public boolean advanceTo(long endingTime) {
83         if (endingTime - currentTime < 0 || enqueuedTaskCount() < 1) {
84             return false;
85         }
86 
87         int runCount = 0;
88         while (nextTaskIsScheduledBefore(endingTime)) {
89             runOneTask();
90             ++runCount;
91         }
92         currentTime = endingTime;
93 
94         return runCount > 0;
95     }
96 
runOneTask()97     public boolean runOneTask() {
98         if (enqueuedTaskCount() < 1) {
99             return false;
100         }
101 
102         PostedRunnable postedRunnable = postedRunnables.remove(0);
103         currentTime = postedRunnable.scheduledTime;
104         postedRunnable.run();
105         return true;
106     }
107 
runTasks(int howMany)108     public boolean runTasks(int howMany) {
109         if (enqueuedTaskCount() < howMany) {
110             return false;
111         }
112 
113         while (howMany > 0) {
114             PostedRunnable postedRunnable = postedRunnables.remove(0);
115             currentTime = postedRunnable.scheduledTime;
116             postedRunnable.run();
117             howMany--;
118         }
119         return true;
120     }
121 
enqueuedTaskCount()122     public int enqueuedTaskCount() {
123         return postedRunnables.size();
124     }
125 
areAnyRunnable()126     public boolean areAnyRunnable() {
127         return nextTaskIsScheduledBefore(currentTime);
128     }
129 
reset()130     public void reset() {
131         postedRunnables.clear();
132         paused = false;
133     }
134 
size()135     public int size() {
136         return postedRunnables.size();
137     }
138 
139     class PostedRunnable implements Comparable<PostedRunnable> {
140         Runnable runnable;
141         long scheduledTime;
142 
PostedRunnable(Runnable runnable, long scheduledTime)143         PostedRunnable(Runnable runnable, long scheduledTime) {
144             this.runnable = runnable;
145             this.scheduledTime = scheduledTime;
146         }
147 
148         @Override
compareTo(PostedRunnable postedRunnable)149         public int compareTo(PostedRunnable postedRunnable) {
150             return (int) (scheduledTime - postedRunnable.scheduledTime);
151         }
152 
run()153         public void run() {
154             runnable.run();
155         }
156     }
157 
nextTaskIsScheduledBefore(long endingTime)158     private boolean nextTaskIsScheduledBefore(long endingTime) {
159         return enqueuedTaskCount() > 0 && postedRunnables.get(0).scheduledTime <= endingTime;
160     }
161 }
162