Skip to content

Commit 65c0ceb

Browse files
authored
Merge 746c5d0 into 3548754
2 parents 3548754 + 746c5d0 commit 65c0ceb

File tree

2 files changed

+60
-16
lines changed

2 files changed

+60
-16
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotUtils.java

+59-16
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
import android.graphics.Bitmap;
66
import android.graphics.Canvas;
77
import android.os.Build;
8+
import android.os.Handler;
9+
import android.os.HandlerThread;
10+
import android.view.PixelCopy;
811
import android.view.View;
12+
import android.view.Window;
13+
914
import androidx.annotation.Nullable;
1015
import io.sentry.ILogger;
1116
import io.sentry.SentryLevel;
@@ -14,6 +19,8 @@
1419
import java.io.ByteArrayOutputStream;
1520
import java.util.concurrent.CountDownLatch;
1621
import java.util.concurrent.TimeUnit;
22+
import java.util.concurrent.atomic.AtomicBoolean;
23+
1724
import org.jetbrains.annotations.ApiStatus;
1825
import org.jetbrains.annotations.NotNull;
1926

@@ -38,13 +45,14 @@ public class ScreenshotUtils {
3845

3946
if (!isActivityValid(activity, buildInfoProvider)
4047
|| activity.getWindow() == null
41-
|| activity.getWindow().getDecorView() == null
42-
|| activity.getWindow().getDecorView().getRootView() == null) {
48+
|| activity.getWindow().peekDecorView() == null
49+
|| activity.getWindow().peekDecorView().getRootView() == null) {
4350
logger.log(SentryLevel.DEBUG, "Activity isn't valid, not taking screenshot.");
4451
return null;
4552
}
4653

47-
final View view = activity.getWindow().getDecorView().getRootView();
54+
final Window window = activity.getWindow();
55+
final View view = window.peekDecorView().getRootView();
4856
if (view.getWidth() <= 0 || view.getHeight() <= 0) {
4957
logger.log(SentryLevel.DEBUG, "View's width and height is zeroed, not taking screenshot.");
5058
return null;
@@ -55,20 +63,55 @@ public class ScreenshotUtils {
5563
final Bitmap bitmap =
5664
Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
5765

58-
final Canvas canvas = new Canvas(bitmap);
59-
if (mainThreadChecker.isMainThread()) {
60-
view.draw(canvas);
66+
final @NotNull CountDownLatch latch = new CountDownLatch(1);
67+
68+
// Use Pixel Copy API on new devices, fallback to canvas rendering on older ones
69+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
70+
71+
final HandlerThread thread = new HandlerThread("screenshot");
72+
thread.start();
73+
74+
boolean success = false;
75+
try {
76+
final Handler handler = new Handler(thread.getLooper());
77+
final AtomicBoolean copyResultSuccess = new AtomicBoolean(false);
78+
79+
PixelCopy.request(
80+
window,
81+
bitmap, copyResult -> {
82+
copyResultSuccess.set(copyResult == PixelCopy.SUCCESS);
83+
latch.countDown();
84+
},
85+
handler);
86+
87+
success = latch.await(CAPTURE_TIMEOUT_MS, TimeUnit.MILLISECONDS) && copyResultSuccess.get();
88+
} catch (InterruptedException e) {
89+
// ignored
90+
} finally {
91+
thread.quit();
92+
}
93+
94+
if (!success) {
95+
return null;
96+
}
6197
} else {
62-
final @NotNull CountDownLatch latch = new CountDownLatch(1);
63-
activity.runOnUiThread(
64-
() -> {
65-
try {
66-
view.draw(canvas);
67-
latch.countDown();
68-
} catch (Throwable e) {
69-
logger.log(SentryLevel.ERROR, "Taking screenshot failed (view.draw).", e);
70-
}
71-
});
98+
final Canvas canvas = new Canvas(bitmap);
99+
if (mainThreadChecker.isMainThread()) {
100+
view.draw(canvas);
101+
latch.countDown();
102+
} else {
103+
activity.runOnUiThread(
104+
() -> {
105+
try {
106+
view.draw(canvas);
107+
} catch (Throwable e) {
108+
logger.log(SentryLevel.ERROR, "Taking screenshot failed (view.draw).", e);
109+
} finally {
110+
latch.countDown();
111+
}
112+
});
113+
}
114+
72115
if (!latch.await(CAPTURE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
73116
return null;
74117
}

sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class ScreenshotEventProcessorTest {
4646
whenever(rootView.height).thenReturn(1)
4747
whenever(view.rootView).thenReturn(rootView)
4848
whenever(window.decorView).thenReturn(view)
49+
whenever(window.peekDecorView()).thenReturn(view)
4950
whenever(activity.window).thenReturn(window)
5051
whenever(activity.runOnUiThread(any())).then {
5152
it.getArgument<Runnable>(0).run()

0 commit comments

Comments
 (0)