Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using PixelCopy in conjunction with ShadowPackageManager.addActivityIfNotPresent and robolectric.screenshot.hwrdr.native results in capturing an empty image. #8982

Open
takahirom opened this issue Apr 12, 2024 · 3 comments

Comments

@takahirom
Copy link
Contributor

takahirom commented Apr 12, 2024

Description

While using a Manifest file to add an Activity allows for successful screenshot captures, employing ShadowPackageManager.addActivityIfNotPresent results in a transparent image instead of the expected bitmap.

Steps to Reproduce

Use following settings and code.

  • robolectric.screenshot.hwrdr.native=true
  • ShadowPackageManager.addActivityIfNotPresent
  • PixelCopy.request

Please add this test into ActivityScenarioTest in Robolectric repo to reproduce the issue

  static class CustomActivity extends Activity {
    @Override public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      View view = new View(this);
      view.setBackgroundColor(0xffff0000);
      view.layout(0, 0, 100, 100);
      setContentView(view);
    }
  }

  @GraphicsMode(GraphicsMode.Mode.NATIVE) @Test
  public void hasPixelWhenUsingCustomAddingActivityHardwareRendering() {
    // **If we use false here, the test will pass.**
    System.setProperty("robolectric.screenshot.hwrdr.native", "true");
    Application application = ApplicationProvider.getApplicationContext();
    ((ShadowPackageManager) Shadows.shadowOf(
        application.getPackageManager())).addActivityIfNotPresent(
        new Intent(application, CustomActivity.class).resolveActivity(
            application.getPackageManager()));

    try (ActivityScenario<CustomActivity> activityScenario = ActivityScenario.launch(
        CustomActivity.class)) {
      activityScenario.onActivity(activity -> {
        Window window = activity.getWindow();
        View decorView = window.getDecorView();
        Espresso.onIdle();
        Bitmap viewDrawBitmap = Bitmap.createBitmap(decorView.getWidth(), decorView.getHeight(),
            Bitmap.Config.ARGB_8888);
        decorView.draw(new Canvas(viewDrawBitmap));

        Bitmap pixelCopyBitmap = Bitmap.createBitmap(decorView.getWidth(), decorView.getHeight(),
            Bitmap.Config.ARGB_8888);
        CountDownLatch latch = new CountDownLatch(1);
        PixelCopy.request(window, pixelCopyBitmap, copyResult -> latch.countDown(),
            new Handler(Looper.getMainLooper()));
        try {
          latch.await(1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
        int pixelCopyBitmapPixel = pixelCopyBitmap.getPixel(0, 0);

        int viewDrawBitmapPixel = viewDrawBitmap.getPixel(0, 0);
        System.out.println("viewDrawBitmapPixel: " + Integer.toHexString(viewDrawBitmapPixel));
        System.out.println("pixelCopyBitmapPixel: " + Integer.toHexString(pixelCopyBitmapPixel));
        assertThat(pixelCopyBitmapPixel).isEqualTo(viewDrawBitmapPixel);
        pixelCopyBitmap.recycle();
      });
    }
  }

Result

> Task :integration_tests:androidx_test:testDebugUnitTest
viewDrawBitmapPixel: ff191c1e
pixelCopyBitmapPixel: 0

expected: -15131618
but was : 0
expected: -15131618
but was : 0
	at org.robolectric.integrationtests.axt.ActivityScenarioTest.lambda$hasPixelWhenUsingCustomAddingActivityHardwareRendering$16(ActivityScenarioTest.java:419)
	at androidx.test.core.app.ActivityScenario.lambda$onActivity$2$androidx-test-core-app-ActivityScenario(ActivityScenario.java:789)

Robolectric & Android Version

Robolectric master branch 03bbc9c (current latest commit)

Link to a public git repo demonstrating the problem:

@hoisie
Copy link
Contributor

hoisie commented Apr 12, 2024

Thanks @takahirom for this issue. We will definitely look at this issue. Overall I would say that HW rendering in Robolectric is still experimental. There are currently a lot of issues with it and the APIs are subject to change.

@hoisie
Copy link
Contributor

hoisie commented Apr 23, 2024

Hi @takahirom,

@ralf-at-android and I looked at this issue just now. The root cause is that when Activities are created in the manner described in your test, the Window of the Activity does not contain FLAG_HARDWARE_ACCELERATED.

If you use this code to set up the Activity, the test passes:

    ActivityController<CustomActivity> customActivityController =
        Robolectric.buildActivity(CustomActivity.class);
    customActivityController
        .get()
        .getWindow()
        .setFlags(
            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
    CustomActivity activity = customActivityController.setup().get();
    

When adding the Activity to an AndroidManifest.xml, the FLAG_HARDWARE_ACCELERATED flag of ActivityInfo gets properly added I believe.

@ralf-at-android and I are still considering how to resolve this issue. FLAG_HARDWARE_ACCELERATED is default to true since Android SDK 14 (ice cream sandwich), so this should also be the default for all Activities in Robolectric. However, that may be a breaking change if somehow tests depend on activities not supporting FLAG_HARDWARE_ACCELERATED. We will look into this issue more.

@takahirom
Copy link
Contributor Author

Thanks. I edited a comment on an issue that appears in the logs when you forget to add an Activity to the Manifest in Robolectric.
#4736 (comment)

copybara-service bot pushed a commit that referenced this issue May 8, 2024
…Present

Previously, only Activities that were present in an AndroidManifest.xml would
contain the FLAG_HARDWARE_ACCELERATED window flag, which gets
populated by PackageParser. This means that Activities not
present in a manifest could not be rendered with a HardwareRenderer, as it
would get ignored due to the missing flag. This meant that the HW rendering
version of PixelCopy would not operate on that Activity.

The FLAG_HARDWARE_ACCELERATED was true by default since
Android ICS (SDK 14), so it should be true by default in Robolectric
as well.

Fixes #8982

PiperOrigin-RevId: 628494031
copybara-service bot pushed a commit that referenced this issue May 8, 2024
…Present

Previously, only Activities that were present in an AndroidManifest.xml would
contain the FLAG_HARDWARE_ACCELERATED window flag, which gets
populated by PackageParser. This means that Activities not
present in a manifest could not be rendered with a HardwareRenderer, as it
would get ignored due to the missing flag. This meant that the HW rendering
version of PixelCopy would not operate on that Activity.

The FLAG_HARDWARE_ACCELERATED was true by default since
Android ICS (SDK 14), so it should be true by default in Robolectric
as well.

Fixes #8982

PiperOrigin-RevId: 628494031
copybara-service bot pushed a commit that referenced this issue May 8, 2024
…Present

Previously, only Activities that were present in an AndroidManifest.xml would
contain the FLAG_HARDWARE_ACCELERATED window flag, which gets
populated by PackageParser. This means that Activities not
present in a manifest could not be rendered with a HardwareRenderer, as it
would get ignored due to the missing flag. This meant that the HW rendering
version of PixelCopy would not operate on that Activity.

The FLAG_HARDWARE_ACCELERATED was true by default since
Android ICS (SDK 14), so it should be true by default in Robolectric
as well.

Fixes #8982

PiperOrigin-RevId: 628494031
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants