1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.JELLY_BEAN; 4 import static android.os.Build.VERSION_CODES.LOLLIPOP; 5 import static android.os.Build.VERSION_CODES.M; 6 import static org.robolectric.shadow.api.Shadow.directlyOn; 7 import static org.robolectric.shadow.api.Shadow.invokeConstructor; 8 9 import android.R; 10 import android.app.Activity; 11 import android.app.ActivityThread; 12 import android.app.Application; 13 import android.app.Dialog; 14 import android.app.Fragment; 15 import android.app.Instrumentation; 16 import android.content.ComponentName; 17 import android.content.Context; 18 import android.content.Intent; 19 import android.content.pm.ActivityInfo; 20 import android.content.pm.PackageManager; 21 import android.content.pm.PackageManager.NameNotFoundException; 22 import android.content.res.Configuration; 23 import android.database.Cursor; 24 import android.os.Build; 25 import android.os.Bundle; 26 import android.os.IBinder; 27 import android.text.Selection; 28 import android.text.SpannableStringBuilder; 29 import android.view.LayoutInflater; 30 import android.view.Menu; 31 import android.view.MenuInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.view.ViewRootImpl; 35 import android.view.Window; 36 import com.android.internal.app.IVoiceInteractor; 37 import java.lang.reflect.InvocationTargetException; 38 import java.lang.reflect.Method; 39 import java.util.ArrayList; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Map; 43 import org.robolectric.RuntimeEnvironment; 44 import org.robolectric.annotation.HiddenApi; 45 import org.robolectric.annotation.Implementation; 46 import org.robolectric.annotation.Implements; 47 import org.robolectric.annotation.RealObject; 48 import org.robolectric.fakes.RoboMenuItem; 49 import org.robolectric.util.ReflectionHelpers; 50 51 @Implements(Activity.class) 52 public class ShadowActivity extends ShadowContextThemeWrapper { 53 54 @RealObject 55 protected Activity realActivity; 56 57 private int resultCode; 58 private Intent resultIntent; 59 private Activity parent; 60 private boolean finishWasCalled; 61 private List<IntentForResult> startedActivitiesForResults = new ArrayList<>(); 62 private Map<Intent.FilterComparison, Integer> intentRequestCodeMap = new HashMap<>(); 63 private int requestedOrientation = -1; 64 private View currentFocus; 65 private Integer lastShownDialogId = null; 66 private int pendingTransitionEnterAnimResId = -1; 67 private int pendingTransitionExitAnimResId = -1; 68 private Object lastNonConfigurationInstance; 69 private Map<Integer, Dialog> dialogForId = new HashMap<>(); 70 private ArrayList<Cursor> managedCursors = new ArrayList<>(); 71 private int mDefaultKeyMode = Activity.DEFAULT_KEYS_DISABLE; 72 private SpannableStringBuilder mDefaultKeySsb = null; 73 private int streamType = -1; 74 private boolean mIsTaskRoot = true; 75 private Menu optionsMenu; 76 private ComponentName callingActivity; 77 78 @Implementation 79 public void __constructor__() { 80 invokeConstructor(Activity.class, realActivity); 81 } 82 83 public void setApplication(Application application) { 84 ReflectionHelpers.setField(realActivity, "mApplication", application); 85 } 86 87 public void callAttach(Intent intent) { 88 int apiLevel = RuntimeEnvironment.getApiLevel(); 89 Application application = RuntimeEnvironment.application; 90 Context baseContext = RuntimeEnvironment.application.getBaseContext(); 91 Class<?> nonConfigurationInstancesClass = getNonConfigurationClass(); 92 93 ActivityInfo activityInfo; 94 try { 95 activityInfo = application.getPackageManager().getActivityInfo(new ComponentName(application.getPackageName(), realActivity.getClass().getName()), PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); 96 } catch (NameNotFoundException e) { 97 throw new RuntimeException(); 98 } 99 100 CharSequence activityTitle = activityInfo.loadLabel(baseContext.getPackageManager()); 101 102 if (apiLevel <= Build.VERSION_CODES.KITKAT) { 103 ReflectionHelpers.callInstanceMethod(Activity.class, realActivity, "attach", 104 ReflectionHelpers.ClassParameter.from(Context.class, baseContext), 105 ReflectionHelpers.ClassParameter.from(ActivityThread.class, RuntimeEnvironment.getActivityThread()), 106 ReflectionHelpers.ClassParameter.from(Instrumentation.class, new Instrumentation()), 107 ReflectionHelpers.ClassParameter.from(IBinder.class, null), 108 ReflectionHelpers.ClassParameter.from(int.class, 0), 109 ReflectionHelpers.ClassParameter.from(Application.class, application), 110 ReflectionHelpers.ClassParameter.from(Intent.class, intent), 111 ReflectionHelpers.ClassParameter.from(ActivityInfo.class, activityInfo), 112 ReflectionHelpers.ClassParameter.from(CharSequence.class, activityTitle), 113 ReflectionHelpers.ClassParameter.from(Activity.class, null), 114 ReflectionHelpers.ClassParameter.from(String.class, "id"), 115 ReflectionHelpers.ClassParameter.from(nonConfigurationInstancesClass, null), 116 ReflectionHelpers.ClassParameter.from(Configuration.class, application.getResources().getConfiguration())); 117 } else if (apiLevel <= Build.VERSION_CODES.LOLLIPOP) { 118 ReflectionHelpers.callInstanceMethod(Activity.class, realActivity, "attach", 119 ReflectionHelpers.ClassParameter.from(Context.class, baseContext), 120 ReflectionHelpers.ClassParameter.from(ActivityThread.class, RuntimeEnvironment.getActivityThread()), 121 ReflectionHelpers.ClassParameter.from(Instrumentation.class, new Instrumentation()), 122 ReflectionHelpers.ClassParameter.from(IBinder.class, null), 123 ReflectionHelpers.ClassParameter.from(int.class, 0), 124 ReflectionHelpers.ClassParameter.from(Application.class, application), 125 ReflectionHelpers.ClassParameter.from(Intent.class, intent), 126 ReflectionHelpers.ClassParameter.from(ActivityInfo.class, activityInfo), 127 ReflectionHelpers.ClassParameter.from(CharSequence.class, activityTitle), 128 ReflectionHelpers.ClassParameter.from(Activity.class, null), 129 ReflectionHelpers.ClassParameter.from(String.class, "id"), 130 ReflectionHelpers.ClassParameter.from(nonConfigurationInstancesClass, null), 131 ReflectionHelpers.ClassParameter.from(Configuration.class, application.getResources().getConfiguration()), 132 ReflectionHelpers.ClassParameter.from(IVoiceInteractor.class, null)); // ADDED 133 } else if (apiLevel <= Build.VERSION_CODES.M) { 134 ReflectionHelpers.callInstanceMethod(Activity.class, realActivity, "attach", 135 ReflectionHelpers.ClassParameter.from(Context.class, baseContext), 136 ReflectionHelpers.ClassParameter.from(ActivityThread.class, RuntimeEnvironment.getActivityThread()), 137 ReflectionHelpers.ClassParameter.from(Instrumentation.class, new Instrumentation()), 138 ReflectionHelpers.ClassParameter.from(IBinder.class, null), 139 ReflectionHelpers.ClassParameter.from(int.class, 0), 140 ReflectionHelpers.ClassParameter.from(Application.class, application), 141 ReflectionHelpers.ClassParameter.from(Intent.class, intent), 142 ReflectionHelpers.ClassParameter.from(ActivityInfo.class, activityInfo), 143 ReflectionHelpers.ClassParameter.from(CharSequence.class, activityTitle), 144 ReflectionHelpers.ClassParameter.from(Activity.class, null), 145 ReflectionHelpers.ClassParameter.from(String.class, "id"), 146 ReflectionHelpers.ClassParameter.from(nonConfigurationInstancesClass, null), 147 ReflectionHelpers.ClassParameter.from(Configuration.class, application.getResources().getConfiguration()), 148 ReflectionHelpers.ClassParameter.from(String.class, "referrer"), 149 ReflectionHelpers.ClassParameter.from(IVoiceInteractor.class, null)); // SAME AS LOLLIPOP --------------------------- 150 } else if (apiLevel <= Build.VERSION_CODES.N_MR1) { 151 ReflectionHelpers.callInstanceMethod(Activity.class, realActivity, "attach", 152 ReflectionHelpers.ClassParameter.from(Context.class, baseContext), 153 ReflectionHelpers.ClassParameter.from(ActivityThread.class, RuntimeEnvironment.getActivityThread()), 154 ReflectionHelpers.ClassParameter.from(Instrumentation.class, new Instrumentation()), 155 ReflectionHelpers.ClassParameter.from(IBinder.class, null), 156 ReflectionHelpers.ClassParameter.from(int.class, 0), 157 ReflectionHelpers.ClassParameter.from(Application.class, application), 158 ReflectionHelpers.ClassParameter.from(Intent.class, intent), 159 ReflectionHelpers.ClassParameter.from(ActivityInfo.class, activityInfo), 160 ReflectionHelpers.ClassParameter.from(CharSequence.class, activityTitle), 161 ReflectionHelpers.ClassParameter.from(Activity.class, null), 162 ReflectionHelpers.ClassParameter.from(String.class, "id"), 163 ReflectionHelpers.ClassParameter.from(nonConfigurationInstancesClass, null), 164 ReflectionHelpers.ClassParameter.from(Configuration.class, application.getResources().getConfiguration()), 165 ReflectionHelpers.ClassParameter.from(String.class, "referrer"), 166 ReflectionHelpers.ClassParameter.from(IVoiceInteractor.class, null), 167 ReflectionHelpers.ClassParameter.from(Window.class, null) // ADDED 168 ); 169 } else if (apiLevel >= Build.VERSION_CODES.O) { 170 ReflectionHelpers.callInstanceMethod(Activity.class, realActivity, "attach", 171 ReflectionHelpers.ClassParameter.from(Context.class, baseContext), 172 ReflectionHelpers.ClassParameter.from(ActivityThread.class, RuntimeEnvironment.getActivityThread()), 173 ReflectionHelpers.ClassParameter.from(Instrumentation.class, new Instrumentation()), 174 ReflectionHelpers.ClassParameter.from(IBinder.class, null), 175 ReflectionHelpers.ClassParameter.from(int.class, 0), 176 ReflectionHelpers.ClassParameter.from(Application.class, application), 177 ReflectionHelpers.ClassParameter.from(Intent.class, intent), 178 ReflectionHelpers.ClassParameter.from(ActivityInfo.class, activityInfo), 179 ReflectionHelpers.ClassParameter.from(CharSequence.class, activityTitle), 180 ReflectionHelpers.ClassParameter.from(Activity.class, null), 181 ReflectionHelpers.ClassParameter.from(String.class, "id"), 182 ReflectionHelpers.ClassParameter.from(nonConfigurationInstancesClass, null), 183 ReflectionHelpers.ClassParameter.from(Configuration.class, application.getResources().getConfiguration()), 184 ReflectionHelpers.ClassParameter.from(String.class, "referrer"), 185 ReflectionHelpers.ClassParameter.from(IVoiceInteractor.class, null), 186 ReflectionHelpers.ClassParameter.from(Window.class, null), 187 ReflectionHelpers.ClassParameter.from(ViewRootImpl.ActivityConfigCallback.class, null) // ADDED 188 ); 189 } else { 190 throw new RuntimeException("Could not find AndroidRuntimeAdapter for API level: " + apiLevel); 191 } 192 193 int theme = activityInfo.getThemeResource(); 194 if (theme != 0) { 195 realActivity.setTheme(theme); 196 } 197 } 198 199 private Class<?> getNonConfigurationClass() { 200 try { 201 return getClass().getClassLoader().loadClass("android.app.Activity$NonConfigurationInstances"); 202 } catch (ClassNotFoundException e) { 203 throw new RuntimeException(e); 204 } 205 } 206 207 public void setCallingActivity(ComponentName activityName) { 208 callingActivity = activityName; 209 } 210 211 @Implementation 212 public ComponentName getCallingActivity() { 213 return callingActivity; 214 } 215 216 @Implementation 217 public void setDefaultKeyMode(int keyMode) { 218 mDefaultKeyMode = keyMode; 219 220 // Some modes use a SpannableStringBuilder to track & dispatch input events 221 // This list must remain in sync with the switch in onKeyDown() 222 switch (mDefaultKeyMode) { 223 case Activity.DEFAULT_KEYS_DISABLE: 224 case Activity.DEFAULT_KEYS_SHORTCUT: 225 mDefaultKeySsb = null; // not used in these modes 226 break; 227 case Activity.DEFAULT_KEYS_DIALER: 228 case Activity.DEFAULT_KEYS_SEARCH_LOCAL: 229 case Activity.DEFAULT_KEYS_SEARCH_GLOBAL: 230 mDefaultKeySsb = new SpannableStringBuilder(); 231 Selection.setSelection(mDefaultKeySsb, 0); 232 break; 233 default: 234 throw new IllegalArgumentException(); 235 } 236 } 237 238 public int getDefaultKeymode() { 239 return mDefaultKeyMode; 240 } 241 242 @Implementation 243 public final void setResult(int resultCode) { 244 this.resultCode = resultCode; 245 } 246 247 @Implementation 248 public final void setResult(int resultCode, Intent data) { 249 this.resultCode = resultCode; 250 this.resultIntent = data; 251 } 252 253 @Implementation 254 public LayoutInflater getLayoutInflater() { 255 return LayoutInflater.from(realActivity); 256 } 257 258 @Implementation 259 public MenuInflater getMenuInflater() { 260 return new MenuInflater(realActivity); 261 } 262 263 /** 264 * Checks to ensure that the{@code contentView} has been set 265 * 266 * @param id ID of the view to find 267 * @return the view 268 * @throws RuntimeException if the {@code contentView} has not been called first 269 */ 270 @Implementation 271 public View findViewById(int id) { 272 return getWindow().findViewById(id); 273 } 274 275 @Implementation 276 public final Activity getParent() { 277 return parent; 278 } 279 280 /** 281 * Allow setting of Parent fragmentActivity (for unit testing purposes only) 282 * 283 * @param parent Parent fragmentActivity to set on this fragmentActivity 284 */ 285 @HiddenApi @Implementation 286 public void setParent(Activity parent) { 287 this.parent = parent; 288 } 289 290 @Implementation 291 public void onBackPressed() { 292 finish(); 293 } 294 295 @Implementation 296 public void finish() { 297 finishWasCalled = true; 298 } 299 300 @Implementation(minSdk = LOLLIPOP) 301 public void finishAndRemoveTask() { 302 finishWasCalled = true; 303 } 304 305 @Implementation(minSdk = JELLY_BEAN) 306 public void finishAffinity() { 307 finishWasCalled = true; 308 } 309 310 public void resetIsFinishing() { 311 finishWasCalled = false; 312 } 313 314 /** 315 * @return whether {@link #finish()} was called 316 */ 317 @Implementation 318 public boolean isFinishing() { 319 return finishWasCalled; 320 } 321 322 /** 323 * Constructs a new Window (a {@link com.android.internal.policy.impl.PhoneWindow}) if no window has previously been 324 * set. 325 * 326 * @return the window associated with this Activity 327 */ 328 @Implementation 329 public Window getWindow() { 330 Window window = directlyOn(realActivity, Activity.class).getWindow(); 331 332 if (window == null) { 333 try { 334 window = ShadowWindow.create(realActivity); 335 setWindow(window); 336 } catch (Exception e) { 337 throw new RuntimeException("Window creation failed!", e); 338 } 339 } 340 341 return window; 342 } 343 344 public void setWindow(Window window) { 345 ReflectionHelpers.setField(realActivity, "mWindow", window); 346 } 347 348 @Implementation 349 public void runOnUiThread(Runnable action) { 350 ShadowApplication.getInstance().getForegroundThreadScheduler().post(action); 351 } 352 353 @Implementation 354 public void setRequestedOrientation(int requestedOrientation) { 355 if (getParent() != null) { 356 getParent().setRequestedOrientation(requestedOrientation); 357 } else { 358 this.requestedOrientation = requestedOrientation; 359 } 360 } 361 362 @Implementation 363 public int getRequestedOrientation() { 364 if (getParent() != null) { 365 return getParent().getRequestedOrientation(); 366 } else { 367 return this.requestedOrientation; 368 } 369 } 370 371 @Implementation 372 public int getTaskId() { 373 return 0; 374 } 375 376 /** 377 * @return the {@code contentView} set by one of the {@code setContentView()} methods 378 */ 379 public View getContentView() { 380 return ((ViewGroup) getWindow().findViewById(R.id.content)).getChildAt(0); 381 } 382 383 /** 384 * @return the {@code resultCode} set by one of the {@code setResult()} methods 385 */ 386 public int getResultCode() { 387 return resultCode; 388 } 389 390 /** 391 * @return the {@code Intent} set by {@link #setResult(int, android.content.Intent)} 392 */ 393 public Intent getResultIntent() { 394 return resultIntent; 395 } 396 397 /** 398 * Consumes and returns the next {@code Intent} on the 399 * started activities for results stack. 400 * 401 * @return the next started {@code Intent} for an activity, wrapped in 402 * an {@link ShadowActivity.IntentForResult} object 403 */ 404 public IntentForResult getNextStartedActivityForResult() { 405 if (startedActivitiesForResults.isEmpty()) { 406 return null; 407 } else { 408 return startedActivitiesForResults.remove(0); 409 } 410 } 411 412 /** 413 * Returns the most recent {@code Intent} started by 414 * {@link #startActivityForResult(Intent, int)} without consuming it. 415 * 416 * @return the most recently started {@code Intent}, wrapped in 417 * an {@link ShadowActivity.IntentForResult} object 418 */ 419 public IntentForResult peekNextStartedActivityForResult() { 420 if (startedActivitiesForResults.isEmpty()) { 421 return null; 422 } else { 423 return startedActivitiesForResults.get(0); 424 } 425 } 426 427 @Implementation 428 public Object getLastNonConfigurationInstance() { 429 return lastNonConfigurationInstance; 430 } 431 432 public void setLastNonConfigurationInstance(Object lastNonConfigurationInstance) { 433 this.lastNonConfigurationInstance = lastNonConfigurationInstance; 434 } 435 436 /** 437 * @param view View to focus. 438 */ 439 public void setCurrentFocus(View view) { 440 currentFocus = view; 441 } 442 443 @Implementation 444 public View getCurrentFocus() { 445 return currentFocus; 446 } 447 448 public int getPendingTransitionEnterAnimationResourceId() { 449 return pendingTransitionEnterAnimResId; 450 } 451 452 public int getPendingTransitionExitAnimationResourceId() { 453 return pendingTransitionExitAnimResId; 454 } 455 456 @Implementation 457 public boolean onCreateOptionsMenu(Menu menu) { 458 optionsMenu = menu; 459 return directlyOn(realActivity, Activity.class).onCreateOptionsMenu(menu); 460 } 461 462 /** 463 * Return the options menu. 464 * 465 * @return Options menu. 466 */ 467 public Menu getOptionsMenu() { 468 return optionsMenu; 469 } 470 471 /** 472 * Perform a click on a menu item. 473 * 474 * @param menuItemResId Menu item resource ID. 475 * @return True if the click was handled, false otherwise. 476 */ 477 public boolean clickMenuItem(int menuItemResId) { 478 if (optionsMenu == null) { 479 throw new RuntimeException( 480 "Activity does not have an options menu! Did you forget to call " + 481 "super.onCreateOptionsMenu(menu) in " + realActivity.getClass().getName() + "?"); 482 } 483 484 final RoboMenuItem item = new RoboMenuItem(menuItemResId); 485 return realActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item); 486 } 487 488 /** 489 * Container object to hold an Intent, together with the requestCode used 490 * in a call to {@code Activity.startActivityForResult(Intent, int)} 491 */ 492 public static class IntentForResult { 493 public Intent intent; 494 public int requestCode; 495 public Bundle options; 496 497 public IntentForResult(Intent intent, int requestCode) { 498 this.intent = intent; 499 this.requestCode = requestCode; 500 this.options = null; 501 } 502 503 public IntentForResult(Intent intent, int requestCode, Bundle options) { 504 this.intent = intent; 505 this.requestCode = requestCode; 506 this.options = options; 507 } 508 } 509 510 @Implementation 511 public void startActivities(Intent[] intents, Bundle options) { 512 for (int i = intents.length - 1; i >= 0; i--) { 513 ShadowApplication.getInstance().startActivity(intents[i], options); 514 } 515 } 516 517 @Implementation 518 public void startActivityForResult(Intent intent, int requestCode) { 519 intentRequestCodeMap.put(new Intent.FilterComparison(intent), requestCode); 520 startedActivitiesForResults.add(new IntentForResult(intent, requestCode)); 521 ShadowApplication.getInstance().startActivity(intent); 522 } 523 524 @Implementation 525 public void startActivityForResult(Intent intent, int requestCode, Bundle options) { 526 intentRequestCodeMap.put(new Intent.FilterComparison(intent), requestCode); 527 startedActivitiesForResults.add(new IntentForResult(intent, requestCode, options)); 528 ShadowApplication.getInstance().startActivity(intent); 529 } 530 531 public void receiveResult(Intent requestIntent, int resultCode, Intent resultIntent) { 532 Integer requestCode = intentRequestCodeMap.get(new Intent.FilterComparison(requestIntent)); 533 if (requestCode == null) { 534 throw new RuntimeException("No intent matches " + requestIntent + " among " + intentRequestCodeMap.keySet()); 535 } 536 537 final ActivityInvoker invoker = new ActivityInvoker(); 538 invoker.call("onActivityResult", Integer.TYPE, Integer.TYPE, Intent.class) 539 .with(requestCode, resultCode, resultIntent); 540 } 541 542 @Implementation 543 public final void showDialog(int id) { 544 showDialog(id, null); 545 } 546 547 @Implementation 548 public final void dismissDialog(int id) { 549 final Dialog dialog = dialogForId.get(id); 550 if (dialog == null) { 551 throw new IllegalArgumentException(); 552 } 553 554 dialog.dismiss(); 555 } 556 557 @Implementation 558 public final void removeDialog(int id) { 559 dialogForId.remove(id); 560 } 561 562 @Implementation 563 public final boolean showDialog(int id, Bundle bundle) { 564 this.lastShownDialogId = id; 565 Dialog dialog = dialogForId.get(id); 566 567 if (dialog == null) { 568 final ActivityInvoker invoker = new ActivityInvoker(); 569 dialog = (Dialog) invoker.call("onCreateDialog", Integer.TYPE).with(id); 570 if (dialog == null) { 571 return false; 572 } 573 if (bundle == null) { 574 invoker.call("onPrepareDialog", Integer.TYPE, Dialog.class).with(id, dialog); 575 } else { 576 invoker.call("onPrepareDialog", Integer.TYPE, Dialog.class, Bundle.class).with(id, dialog, bundle); 577 } 578 579 dialogForId.put(id, dialog); 580 } 581 582 dialog.show(); 583 return true; 584 } 585 586 public void setIsTaskRoot(boolean isRoot) { 587 mIsTaskRoot = isRoot; 588 } 589 590 @Implementation 591 public final boolean isTaskRoot() { 592 return mIsTaskRoot; 593 } 594 595 /** 596 * @return the dialog resource id passed into 597 * {@code Activity.showDialog(int, Bundle)} or {@code Activity.showDialog(int)} 598 */ 599 public Integer getLastShownDialogId() { 600 return lastShownDialogId; 601 } 602 603 public boolean hasCancelledPendingTransitions() { 604 return pendingTransitionEnterAnimResId == 0 && pendingTransitionExitAnimResId == 0; 605 } 606 607 @Implementation 608 public void overridePendingTransition(int enterAnim, int exitAnim) { 609 pendingTransitionEnterAnimResId = enterAnim; 610 pendingTransitionExitAnimResId = exitAnim; 611 } 612 613 public Dialog getDialogById(int dialogId) { 614 return dialogForId.get(dialogId); 615 } 616 617 @Implementation 618 public void recreate() { 619 Bundle outState = new Bundle(); 620 final ActivityInvoker invoker = new ActivityInvoker(); 621 622 invoker.call("onSaveInstanceState", Bundle.class).with(outState); 623 invoker.call("onPause").withNothing(); 624 invoker.call("onStop").withNothing(); 625 626 Object nonConfigInstance = invoker.call("onRetainNonConfigurationInstance").withNothing(); 627 setLastNonConfigurationInstance(nonConfigInstance); 628 629 invoker.call("onDestroy").withNothing(); 630 invoker.call("onCreate", Bundle.class).with(outState); 631 invoker.call("onStart").withNothing(); 632 invoker.call("onRestoreInstanceState", Bundle.class).with(outState); 633 invoker.call("onResume").withNothing(); 634 } 635 636 @Implementation 637 public void startManagingCursor(Cursor c) { 638 managedCursors.add(c); 639 } 640 641 @Implementation 642 public void stopManagingCursor(Cursor c) { 643 managedCursors.remove(c); 644 } 645 646 public List<Cursor> getManagedCursors() { 647 return managedCursors; 648 } 649 650 @Implementation 651 public final void setVolumeControlStream(int streamType) { 652 this.streamType = streamType; 653 } 654 655 @Implementation 656 public final int getVolumeControlStream() { 657 return streamType; 658 } 659 660 @Implementation 661 public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) { 662 startActivityForResult(intent, requestCode); 663 } 664 665 @Implementation 666 public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options) { 667 startActivityForResult(intent, requestCode, options); 668 } 669 670 @Implementation(minSdk = M) 671 public final void requestPermissions(String[] permissions, int requestCode) { 672 } 673 674 private final class ActivityInvoker { 675 private Method method; 676 677 public ActivityInvoker call(final String methodName, final Class... argumentClasses) { 678 try { 679 method = Activity.class.getDeclaredMethod(methodName, argumentClasses); 680 method.setAccessible(true); 681 return this; 682 } catch (NoSuchMethodException e) { 683 throw new RuntimeException(e); 684 } 685 } 686 687 public Object withNothing() { 688 return with(); 689 } 690 691 public Object with(final Object... parameters) { 692 try { 693 return method.invoke(realActivity, parameters); 694 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 695 throw new RuntimeException(e); 696 } 697 } 698 } 699 } 700