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