shadows/framework/src/main/java/org/robolectric/android/controller/ActivityController.java
public ActivityController<T> configurationChange(final Configuration newConfiguration) {
    final Configuration currentConfig = component.getResources().getConfiguration();
    final int changedBits = currentConfig.diff(newConfiguration);
    currentConfig.setTo(newConfiguration);

    // TODO: throw on changedBits == 0 since it non-intuitively calls onConfigurationChanged

    // Can the activity handle itself ALL configuration changes?
    if ((getActivityInfo(component.getApplication()).configChanges & changedBits) == changedBits) {
      shadowMainLooper.runPaused(() -> component.onConfigurationChanged(newConfiguration));

      return this;
    } else {
      @SuppressWarnings("unchecked")
      final T recreatedActivity = (T) ReflectionHelpers.callConstructor(component.getClass());
      final _Activity_ _recreatedActivity_ = reflector(_Activity_.class, recreatedActivity);

      shadowMainLooper.runPaused(
          new Runnable() {
            @Override
            public void run() {
              // Set flags
              ReflectionHelpers.setField(
                  Activity.class, component, "mChangingConfigurations", true);
              ReflectionHelpers.setField(
                  Activity.class, component, "mConfigChangeFlags", changedBits);

              // Perform activity destruction
              final Bundle outState = new Bundle();

              // The order of onPause/onStop/onSaveInstanceState is undefined, but is usually:
              // onPause -> onSaveInstanceState -> onStop
              _component_.performPause();
              _component_.performSaveInstanceState(outState);
              if (RuntimeEnvironment.getApiLevel() <= M) {
                _component_.performStop();
              } else if (RuntimeEnvironment.getApiLevel() <= O_MR1) {
                _component_.performStop(true);
              } else {
                _component_.performStop(true, "configurationChange");
              }

              // This is the true and complete retained state, including loaders and retained
              // fragments.
              final Object nonConfigInstance = _component_.retainNonConfigurationInstances();
              // This is the activity's "user" state
              final Object activityConfigInstance =
                  nonConfigInstance == null
                      ? null // No framework or user state.
                      : reflector(_NonConfigurationInstances_.class, nonConfigInstance)
                          .getActivity();

              _component_.performDestroy();

              // Restore theme in case it was set in the test manually.
              // This is not technically what happens but is purely to make this easier to use in
              // Robolectric.
              ShadowContextThemeWrapper shadowContextThemeWrapper = Shadow.extract(component);
              int theme = shadowContextThemeWrapper.callGetThemeResId();

              // Setup controller for the new activity
              attached = false;
              component = recreatedActivity;
              _component_ = _recreatedActivity_;

              // TODO: Pass nonConfigurationInstance here instead of setting
              // mLastNonConfigurationInstances directly below. This field must be set before
              // attach. Since current implementation sets it after attach(), initialization is not
              // done correctly. For instance, fragment marked as retained is not retained.
              attach(/*lastNonConfigurationInstances=*/ null);

              if (theme != 0) {
                recreatedActivity.setTheme(theme);
              }

              // Set saved non config instance
              _recreatedActivity_.setLastNonConfigurationInstances(nonConfigInstance);
              ShadowActivity shadowActivity = Shadow.extract(recreatedActivity);
              shadowActivity.setLastNonConfigurationInstance(activityConfigInstance);

              // Create lifecycle
              _recreatedActivity_.performCreate(outState);

              if (RuntimeEnvironment.getApiLevel() <= O_MR1) {

                _recreatedActivity_.performStart();

              } else {
                _recreatedActivity_.performStart("configurationChange");
              }

              _recreatedActivity_.performRestoreInstanceState(outState);
              _recreatedActivity_.onPostCreate(outState);
              if (RuntimeEnvironment.getApiLevel() <= O_MR1) {
                _recreatedActivity_.performResume();
              } else {
                _recreatedActivity_.performResume(true, "configurationChange");
              }
              _recreatedActivity_.onPostResume();
              // TODO: Call visible() too.
            }
          });
    }

    return this;
  }
public ActivityController<T> configurationChange(final Configuration newConfiguration) {
    final Configuration currentConfig = component.getResources().getConfiguration();
    final int changedBits = currentConfig.diff(newConfiguration);
    currentConfig.setTo(newConfiguration);

    // TODO: throw on changedBits == 0 since it non-intuitively calls onConfigurationChanged

    // Can the activity handle itself ALL configuration changes?
    if ((getActivityInfo(component.getApplication()).configChanges & changedBits) == changedBits) {
      shadowMainLooper.runPaused(() -> component.onConfigurationChanged(newConfiguration));

      return this;
    } else {
      @SuppressWarnings("unchecked")
      final T recreatedActivity = (T) ReflectionHelpers.callConstructor(component.getClass());
      final _Activity_ _recreatedActivity_ = reflector(_Activity_.class, recreatedActivity);

      shadowMainLooper.runPaused(
          new Runnable() {
            @Override
            public void run() {
              // Set flags
              ReflectionHelpers.setField(
                  Activity.class, component, "mChangingConfigurations", true);
              ReflectionHelpers.setField(
                  Activity.class, component, "mConfigChangeFlags", changedBits);

              // Perform activity destruction
              final Bundle outState = new Bundle();

              // The order of onPause/onStop/onSaveInstanceState is undefined, but is usually:
              // onPause -> onSaveInstanceState -> onStop
              _component_.performPause();
              _component_.performSaveInstanceState(outState);
              if (RuntimeEnvironment.getApiLevel() <= M) {
                _component_.performStop();
              } else if (RuntimeEnvironment.getApiLevel() <= O_MR1) {
                _component_.performStop(true);
              } else {
                _component_.performStop(true, "configurationChange");
              }

              // This is the true and complete retained state, including loaders and retained
              // fragments.
              final Object nonConfigInstance = _component_.retainNonConfigurationInstances();
              // This is the activity's "user" state
              final Object activityConfigInstance =
                  nonConfigInstance == null
                      ? null // No framework or user state.
                      : reflector(_NonConfigurationInstances_.class, nonConfigInstance)
                          .getActivity();

              _component_.performDestroy();

              // Restore theme in case it was set in the test manually.
              // This is not technically what happens but is purely to make this easier to use in
              // Robolectric.
              ShadowContextThemeWrapper shadowContextThemeWrapper = Shadow.extract(component);
              int theme = shadowContextThemeWrapper.callGetThemeResId();

              // Setup controller for the new activity
              attached = false;
              component = recreatedActivity;
              _component_ = _recreatedActivity_;

              // TODO: Pass nonConfigurationInstance here instead of setting
              // mLastNonConfigurationInstances directly below. This field must be set before
              // attach. Since current implementation sets it after attach(), initialization is not
              // done correctly. For instance, fragment marked as retained is not retained.
              attach(/*lastNonConfigurationInstances=*/ null);

              if (theme != 0) {
                recreatedActivity.setTheme(theme);
              }

              // Set saved non config instance
              _recreatedActivity_.setLastNonConfigurationInstances(nonConfigInstance);
              ShadowActivity shadowActivity = Shadow.extract(recreatedActivity);
              shadowActivity.setLastNonConfigurationInstance(activityConfigInstance);

              // Create lifecycle
              _recreatedActivity_.performCreate(outState);

              if (RuntimeEnvironment.getApiLevel() <= O_MR1) {

                _recreatedActivity_.performStart();

              } else {
                _recreatedActivity_.performStart("configurationChange");
              }

              _recreatedActivity_.performRestoreInstanceState(outState);
              _recreatedActivity_.onPostCreate(outState);
              if (RuntimeEnvironment.getApiLevel() <= O_MR1) {
                _recreatedActivity_.performResume();
              } else {
                _recreatedActivity_.performResume(true, "configurationChange");
              }
              _recreatedActivity_.onPostResume();
              // TODO: Call visible() too.
            }
          });
    }

    return this;
  }
Performs a configuration change on the Activity.

If the activity is configured to handle changes without being recreated,
{@link Activity#onConfigurationChanged(Configuration)} will be called. Otherwise, the activity
is recreated as described [here](https://developer.android.com/guide/topics/resources/runtime-changes.html).

@param newConfiguration The new configuration to be set.
@return ActivityController instance