1package com.xtremelabs.robolectric.shadows;
2
3import android.os.Handler;
4import android.os.Looper;
5import android.os.Message;
6
7import com.xtremelabs.robolectric.Robolectric;
8import com.xtremelabs.robolectric.internal.Implementation;
9import com.xtremelabs.robolectric.internal.Implements;
10import com.xtremelabs.robolectric.internal.RealObject;
11
12import java.util.ArrayList;
13import java.util.Iterator;
14import java.util.List;
15
16import 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)
26public 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
33    public void __constructor__() {
34        this.looper = Looper.myLooper();
35    }
36
37    public void __constructor__(Looper looper) {
38        this.looper = looper;
39    }
40
41    public void __constructor__(Handler.Callback callback) {
42        this.callback = callback;
43    }
44
45    @Implementation
46    public boolean post(Runnable r) {
47        return postDelayed(r, 0);
48    }
49
50    @Implementation
51    public boolean postDelayed(Runnable r, long delayMillis) {
52        return shadowOf(looper).post(r, delayMillis);
53    }
54
55    @Implementation
56    public final boolean postAtFrontOfQueue(Runnable runnable) {
57        return shadowOf(looper).postAtFrontOfQueue(runnable);
58    }
59
60    @Implementation
61    public Message obtainMessage() {
62        return obtainMessage(0);
63    }
64
65    @Implementation
66    public Message obtainMessage(int what) {
67        return obtainMessage(what, null);
68    }
69
70    @Implementation
71    public Message obtainMessage(int what, Object obj) {
72        return obtainMessage(what, 0, 0, obj);
73    }
74
75    @Implementation
76    public Message obtainMessage(int what, int arg1, int arg2) {
77        return obtainMessage(what, arg1, arg2, null);
78    }
79
80    @Implementation
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
92    public final boolean sendMessage(final Message msg) {
93        return sendMessageDelayed(msg, 0L);
94    }
95
96    @Implementation
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
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
121    public final boolean sendEmptyMessage(int what) {
122        return sendEmptyMessageDelayed(what, 0L);
123    }
124
125    @Implementation
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
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
149    public final Looper getLooper() {
150        return looper;
151    }
152
153    @Implementation
154    public final void removeCallbacks(java.lang.Runnable r) {
155        shadowOf(looper).getScheduler().remove(r);
156    }
157
158    @Implementation
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
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
180    public final void removeMessages(int what) {
181        removeMessages(what, null);
182    }
183
184    @Implementation
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     */
199    public static void flush() {
200        idleMainLooper();
201    }
202
203    /**
204     * @see com.xtremelabs.robolectric.shadows.ShadowLooper#idle()
205     */
206    public static void idleMainLooper() {
207        shadowOf(Looper.myLooper()).idle();
208    }
209
210    /**
211     * @see ShadowLooper#runToEndOfTasks() ()
212     */
213    public static void runMainLooperToEndOfTasks() {
214        shadowOf(Looper.myLooper()).runToEndOfTasks();
215    }
216
217
218    /**
219     * @see ShadowLooper#runOneTask() ()
220     */
221    public static void runMainLooperOneTask() {
222        shadowOf(Looper.myLooper()).runOneTask();
223    }
224
225    /**
226     * @see ShadowLooper#runToNextTask() ()
227     */
228    public static void runMainLooperToNextTask() {
229        shadowOf(Looper.myLooper()).runToNextTask();
230    }
231}
232