1 package com.xtremelabs.robolectric.shadows;
2 
3 import android.os.Handler;
4 import android.os.Looper;
5 import android.os.Message;
6 
7 import com.xtremelabs.robolectric.Robolectric;
8 import com.xtremelabs.robolectric.internal.Implementation;
9 import com.xtremelabs.robolectric.internal.Implements;
10 import com.xtremelabs.robolectric.internal.RealObject;
11 
12 import java.util.ArrayList;
13 import java.util.Iterator;
14 import java.util.List;
15 
16 import static com.xtremelabs.robolectric.Robolectric.shadowOf;
17 
18 /**
19  * Shadow for Handler that puts posted {@link Runnable}s into a queue instead of sending them to be handled on a
20  * separate thread.{@link Runnable}s that are scheduled to be executed immediately can be triggered by calling
21  * {@link #idleMainLooper()}.
22  * todo: add utility method to advance time and trigger execution of Runnables scheduled for a time in the future
23  */
24 @SuppressWarnings({"UnusedDeclaration"})
25 @Implements(Handler.class)
26 public class ShadowHandler {
27     @RealObject
28     private Handler realHandler;
29     private Looper looper = Looper.myLooper();
30     private List<Message> messages = new ArrayList<Message>();
31     private Handler.Callback callback;
32 
__constructor__()33     public void __constructor__() {
34         this.looper = Looper.myLooper();
35     }
36 
__constructor__(Looper looper)37     public void __constructor__(Looper looper) {
38         this.looper = looper;
39     }
40 
__constructor__(Handler.Callback callback)41     public void __constructor__(Handler.Callback callback) {
42         this.callback = callback;
43     }
44 
45     @Implementation
post(Runnable r)46     public boolean post(Runnable r) {
47         return postDelayed(r, 0);
48     }
49 
50     @Implementation
postDelayed(Runnable r, long delayMillis)51     public boolean postDelayed(Runnable r, long delayMillis) {
52         return shadowOf(looper).post(r, delayMillis);
53     }
54 
55     @Implementation
postAtFrontOfQueue(Runnable runnable)56     public final boolean postAtFrontOfQueue(Runnable runnable) {
57         return shadowOf(looper).postAtFrontOfQueue(runnable);
58     }
59 
60     @Implementation
obtainMessage()61     public Message obtainMessage() {
62         return obtainMessage(0);
63     }
64 
65     @Implementation
obtainMessage(int what)66     public Message obtainMessage(int what) {
67         return obtainMessage(what, null);
68     }
69 
70     @Implementation
obtainMessage(int what, Object obj)71     public Message obtainMessage(int what, Object obj) {
72         return obtainMessage(what, 0, 0, obj);
73     }
74 
75     @Implementation
obtainMessage(int what, int arg1, int arg2)76     public Message obtainMessage(int what, int arg1, int arg2) {
77         return obtainMessage(what, arg1, arg2, null);
78     }
79 
80     @Implementation
obtainMessage(int what, int arg1, int arg2, Object obj)81     public Message obtainMessage(int what, int arg1, int arg2, Object obj) {
82         Message message = new Message();
83         message.what = what;
84         message.arg1 = arg1;
85         message.arg2 = arg2;
86         message.obj = obj;
87         message.setTarget(realHandler);
88         return message;
89     }
90 
91     @Implementation
sendMessage(final Message msg)92     public final boolean sendMessage(final Message msg) {
93         return sendMessageDelayed(msg, 0L);
94     }
95 
96     @Implementation
sendMessageDelayed(final Message msg, long delayMillis)97     public final boolean sendMessageDelayed(final Message msg, long delayMillis) {
98         Robolectric.shadowOf(msg).setWhen(Robolectric.shadowOf(looper).getScheduler().getCurrentTime()+delayMillis);
99         messages.add(msg);
100         postDelayed(new Runnable() {
101             @Override
102             public void run() {
103                 if (messages.contains(msg)) {
104                     messages.remove(msg);
105                     routeMessage(msg);
106                 }
107             }
108         }, delayMillis);
109         return true;
110     }
111 
routeMessage(Message msg)112     private void routeMessage(Message msg) {
113         if(callback != null) {
114             callback.handleMessage(msg);
115         } else {
116             realHandler.handleMessage(msg);
117         }
118     }
119 
120     @Implementation
sendEmptyMessage(int what)121     public final boolean sendEmptyMessage(int what) {
122         return sendEmptyMessageDelayed(what, 0L);
123     }
124 
125     @Implementation
sendEmptyMessageDelayed(int what, long delayMillis)126     public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
127         final Message msg = new Message();
128         msg.what = what;
129         return sendMessageDelayed(msg, delayMillis);
130     }
131 
132     @Implementation
sendMessageAtFrontOfQueue(final Message msg)133     public final boolean sendMessageAtFrontOfQueue(final Message msg) {
134         Robolectric.shadowOf(msg).setWhen(Robolectric.shadowOf(looper).getScheduler().getCurrentTime());
135         messages.add(0, msg);
136         postAtFrontOfQueue(new Runnable() {
137             @Override
138             public void run() {
139                 if (messages.contains(msg)) {
140                     messages.remove(msg);
141                     routeMessage(msg);
142                 }
143             }
144         });
145         return true;
146     }
147 
148     @Implementation
getLooper()149     public final Looper getLooper() {
150         return looper;
151     }
152 
153     @Implementation
removeCallbacks(java.lang.Runnable r)154     public final void removeCallbacks(java.lang.Runnable r) {
155         shadowOf(looper).getScheduler().remove(r);
156     }
157 
158     @Implementation
hasMessages(int what)159     public final boolean hasMessages(int what) {
160         for (Message message : messages) {
161             if (message.what == what) {
162                 return true;
163             }
164         }
165         return false;
166     }
167 
168     @Implementation
hasMessages(int what, Object object)169     public final boolean hasMessages(int what, Object object) {
170         for (Message message : messages) {
171             if(message.what == what && message.obj == object) {
172                 return true;
173             }
174         }
175         return false;
176     }
177 
178 
179     @Implementation
removeMessages(int what)180     public final void removeMessages(int what) {
181         removeMessages(what, null);
182     }
183 
184     @Implementation
removeMessages(int what, Object object)185     public final void removeMessages(int what, Object object) {
186         for (Iterator<Message> iterator = messages.iterator(); iterator.hasNext(); ) {
187             Message message = iterator.next();
188             if (message.what == what && (object == null || object.equals(message.obj))) {
189                 iterator.remove();
190             }
191         }
192     }
193 
194 
195 
196     /**
197      * @deprecated use {@link #idleMainLooper()} instead
198      */
flush()199     public static void flush() {
200         idleMainLooper();
201     }
202 
203     /**
204      * @see com.xtremelabs.robolectric.shadows.ShadowLooper#idle()
205      */
idleMainLooper()206     public static void idleMainLooper() {
207         shadowOf(Looper.myLooper()).idle();
208     }
209 
210     /**
211      * @see ShadowLooper#runToEndOfTasks() ()
212      */
runMainLooperToEndOfTasks()213     public static void runMainLooperToEndOfTasks() {
214         shadowOf(Looper.myLooper()).runToEndOfTasks();
215     }
216 
217 
218     /**
219      * @see ShadowLooper#runOneTask() ()
220      */
runMainLooperOneTask()221     public static void runMainLooperOneTask() {
222         shadowOf(Looper.myLooper()).runOneTask();
223     }
224 
225     /**
226      * @see ShadowLooper#runToNextTask() ()
227      */
runMainLooperToNextTask()228     public static void runMainLooperToNextTask() {
229         shadowOf(Looper.myLooper()).runToNextTask();
230     }
231 }
232