ShadowActivity.java revision c6526af59ebcc818c95680ac6f8efe83a0b65644
1package com.xtremelabs.robolectric.shadows; 2 3import android.app.Activity; 4import android.app.Application; 5import android.content.ComponentName; 6import android.content.Intent; 7import android.content.IntentSender; 8import android.view.LayoutInflater; 9import android.view.MenuInflater; 10import android.view.View; 11import android.view.Window; 12import com.xtremelabs.robolectric.Robolectric; 13import com.xtremelabs.robolectric.internal.Implementation; 14import com.xtremelabs.robolectric.internal.Implements; 15import com.xtremelabs.robolectric.internal.RealObject; 16import com.xtremelabs.robolectric.tester.android.view.TestWindow; 17 18import java.lang.reflect.InvocationTargetException; 19import java.lang.reflect.Method; 20import java.util.ArrayList; 21import java.util.HashMap; 22import java.util.List; 23import java.util.Map; 24 25import static com.xtremelabs.robolectric.Robolectric.shadowOf; 26 27 28@SuppressWarnings({"UnusedDeclaration"}) 29@Implements(Activity.class) 30public class ShadowActivity extends ShadowContextWrapper { 31 @RealObject private Activity realActivity; 32 33 private Intent intent; 34 View contentView; 35 private int orientation; 36 private int resultCode; 37 private Intent resultIntent; 38 private Intent startServiceIntent; 39 private Activity parent; 40 private boolean finishWasCalled; 41 private TestWindow window; 42 43 private IntentSender startIntentSenderIntent; 44 private Intent startIntentSenderFillInIntent; 45 private int startIntentSenderFlagsMask; 46 private int startIntentSenderFlagsValues; 47 private int startIntentSenderExtraFlags; 48 private boolean startIntentSenderShouldThrowException = false; 49 50 private List<IntentForResult> startedActivitiesForResults = new ArrayList<IntentForResult>(); 51 52 private Map<Intent, Integer> intentRequestCodeMap = new HashMap<Intent, Integer>(); 53 54 @Implementation 55 public final Application getApplication() { 56 return Robolectric.application; 57 } 58 59 @Override @Implementation 60 public final Application getApplicationContext() { 61 return getApplication(); 62 } 63 64 @Implementation 65 public void setIntent(Intent intent) { 66 this.intent = intent; 67 } 68 69 @Implementation 70 public Intent getIntent() { 71 return intent; 72 } 73 74 @Implementation 75 public int getRequestedOrientation() { 76 return orientation; 77 } 78 79 @Implementation 80 public void setRequestedOrientation( int requestedOrientation ) { 81 orientation = requestedOrientation; 82 } 83 84 85 /** 86 * Sets the {@code contentView} for this {@code Activity} by invoking the 87 * {@link android.view.LayoutInflater} 88 * 89 * @param layoutResID ID of the layout to inflate 90 * @see #getContentView() 91 */ 92 @Implementation 93 public void setContentView(int layoutResID) { 94 contentView = getLayoutInflater().inflate(layoutResID, null); 95 realActivity.onContentChanged(); 96 } 97 98 @Implementation 99 public void setContentView(View view) { 100 contentView = view; 101 realActivity.onContentChanged(); 102 } 103 104 @Implementation 105 public final void setResult(int resultCode) { 106 this.resultCode = resultCode; 107 } 108 109 @Implementation 110 public final void setResult(int resultCode, Intent data) { 111 this.resultCode = resultCode; 112 this.resultIntent = data; 113 } 114 115 @Implementation 116 public LayoutInflater getLayoutInflater() { 117 return LayoutInflater.from(realActivity); 118 } 119 120 @Implementation 121 public MenuInflater getMenuInflater() { 122 return new MenuInflater(realActivity); 123 } 124 125 /** 126 * Checks to ensure that the{@code contentView} has been set 127 * 128 * @param id ID of the view to find 129 * @return the view 130 * @throws RuntimeException if the {@code contentView} has not been called first 131 */ 132 @Implementation 133 public View findViewById(int id) { 134 if (contentView != null) { 135 return contentView.findViewById(id); 136 } else { 137 System.out.println("WARNING: you probably should have called setContentView() first"); 138 Thread.dumpStack(); 139 return null; 140 } 141 } 142 143 @Implementation 144 public final Activity getParent() { 145 return parent; 146 } 147 148 @Implementation 149 public void finish() { 150 finishWasCalled = true; 151 } 152 153 /** 154 * @return whether {@link #finish()} was called 155 */ 156 @Implementation 157 public boolean isFinishing() { 158 return finishWasCalled; 159 } 160 161 /** 162 * Constructs a new Window (a {@link com.xtremelabs.robolectric.tester.android.view.TestWindow}) if no window has previously been 163 * set. 164 * 165 * @return the window associated with this Activity 166 */ 167 @Implementation 168 public Window getWindow() { 169 if (window == null) { 170 window = new TestWindow(realActivity); 171 } 172 return window; 173 } 174 175 @Implementation 176 public void runOnUiThread(Runnable action) { 177 Robolectric.getUiThreadScheduler().post(action); 178 } 179 180 /** 181 * Checks to see if {@code BroadcastListener}s are still registered. 182 * 183 * @throws RuntimeException if any listeners are still registered 184 * @see #assertNoBroadcastListenersRegistered() 185 */ 186 @Implementation 187 public void onDestroy() { 188 assertNoBroadcastListenersRegistered(); 189 } 190 191 /** 192 * Checks the {@code ApplicationContext} to see if {@code BroadcastListener}s are still registered. 193 * 194 * @throws RuntimeException if any listeners are still registered 195 * @see ShadowApplication#assertNoBroadcastListenersRegistered(android.content.Context, String) 196 */ 197 public void assertNoBroadcastListenersRegistered() { 198 shadowOf(getApplicationContext()).assertNoBroadcastListenersRegistered(realActivity, "Activity"); 199 } 200 201 /** 202 * Non-Android accessor. 203 * 204 * @return the {@code contentView} set by one of the {@code setContentView()} methods 205 */ 206 public View getContentView() { 207 return contentView; 208 } 209 210 /** 211 * Non-Android accessor. 212 * 213 * @return the {@code resultCode} set by one of the {@code setResult()} methods 214 */ 215 public int getResultCode() { 216 return resultCode; 217 } 218 219 /** 220 * Non-Android accessor. 221 * 222 * @return the {@code Intent} set by {@link #setResult(int, android.content.Intent)} 223 */ 224 public Intent getResultIntent() { 225 return resultIntent; 226 } 227 228 /** 229 * Non-Android accessor consumes and returns the next {@code Intent} on the 230 * started activities for results stack. 231 * 232 * @return the next started {@code Intent} for an activity, wrapped in 233 * an {@link ShadowActivity.IntentForResult} object 234 */ 235 public IntentForResult getNextStartedActivityForResult() { 236 if (startedActivitiesForResults.isEmpty()) { 237 return null; 238 } else { 239 return startedActivitiesForResults.remove(0); 240 } 241 } 242 243 /** 244 * Non-Android accessor returns the most recent {@code Intent} started by 245 * {@link #startActivityForResult(android.content.Intent, int)} without 246 * consuming it. 247 * 248 * @return the most recently started {@code Intent}, wrapped in 249 * an {@link ShadowActivity.IntentForResult} object 250 */ 251 public IntentForResult peekNextStartedActivityForResult() { 252 if (startedActivitiesForResults.isEmpty()) { 253 return null; 254 } else { 255 return startedActivitiesForResults.get(0); 256 } 257 } 258 259 /** 260 * Container object to hold an Intent, together with the requestCode used 261 * in a call to {@code Activity#startActivityForResult(Intent, int)} 262 */ 263 public class IntentForResult { 264 public Intent intent; 265 public int requestCode; 266 267 public IntentForResult(Intent intent, int requestCode) { 268 this.intent = intent; 269 this.requestCode = requestCode; 270 } 271 } 272 273 @Implementation 274 public void startActivityForResult(Intent intent, int requestCode) { 275 intentRequestCodeMap.put(intent, requestCode); 276 startedActivitiesForResults.add(new IntentForResult(intent, requestCode)); 277 getApplicationContext().startActivity(intent); 278 } 279 280 public void receiveResult(Intent requestIntent, int resultCode, Intent resultIntent) { 281 Integer requestCode = intentRequestCodeMap.get(requestIntent); 282 if (requestCode == null) { 283 throw new RuntimeException("No intent matches " + requestIntent + " among " + intentRequestCodeMap.keySet()); 284 } 285 try { 286 Method method = Activity.class.getDeclaredMethod("onActivityResult", Integer.TYPE, Integer.TYPE, Intent.class); 287 method.setAccessible(true); 288 method.invoke(realActivity, requestCode, resultCode, resultIntent); 289 } catch (IllegalAccessException e) { 290 throw new RuntimeException(e); 291 } catch (InvocationTargetException e) { 292 throw new RuntimeException(e); 293 } catch (NoSuchMethodException e) { 294 throw new RuntimeException(e); 295 } 296 } 297 298 @Implementation 299 public ComponentName startService(Intent service) { 300 startServiceIntent = service; 301 return null; 302 } 303 304 /** 305 * Non-Android accessor. 306 * 307 * @return the {@code Intent} set by {@link #startService(android.content.Intent)} 308 */ 309 public Intent getStartServiceIntent() { 310 return startServiceIntent; 311 } 312 313 @Implementation 314 public void startIntentSender (IntentSender intent, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) 315 throws IntentSender.SendIntentException { 316 startIntentSenderIntent = intent; 317 startIntentSenderFillInIntent = fillInIntent; 318 startIntentSenderFlagsMask = flagsMask; 319 startIntentSenderFlagsValues = flagsValues; 320 startIntentSenderExtraFlags = extraFlags; 321 322 if (startIntentSenderShouldThrowException) { 323 throw new IntentSender.SendIntentException(); 324 } 325 } 326 327 public void setStartIntentSenderShouldThrowException(boolean flag) { 328 startIntentSenderShouldThrowException = flag; 329 } 330 331 public IntentSender getStartIntentSenderIntent() { 332 return startIntentSenderIntent; 333 } 334 335 public Intent getStartIntentSenderFillInIntent() { 336 return startIntentSenderFillInIntent; 337 } 338 339 public int getStartIntentSenderFlagsMask() { 340 return startIntentSenderFlagsMask; 341 } 342 343 public int getStartIntentSenderFlagsValues() { 344 return startIntentSenderFlagsValues; 345 } 346 347 public int getStartIntentSenderExtraFlags() { 348 return startIntentSenderExtraFlags; 349 } 350} 351