From d38bb1212588ead1bd320f4763b3bc90409208f1 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Fri, 2 Feb 2024 02:51:04 -0800 Subject: [PATCH] Use curtains to get phone window from decor view Summary: The official api only let you access this window by going through a views activity, but if a doesn't belong to an activity there is no possible way without reflection tricks. Curtains is a nice library that does this reflection adn a lot of the other reflection hacks that the UIdebugger needs. ## The reason this is important When snapshotting a dialog fragment we were using pixel copy using tryCopyViaInternalSurface approach. The issue is this is the lowest level api and doesnt consider 'surface insets' Surface insets seems to be related to the fact that a surface may be bigger than the content it draws. This ended up manifesting like this {F1372530065} the issue is we create a bitmap sized to the decor view, but we were snapshotting the whole surface which is actually a bit bigger due to the black bars / surface insets. When you pass a android.view.Window to pixel copy it has code to account for the surface insets. https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/graphics/java/android/view/PixelCopy.java;l=291;drc=228276a74bc36e8c0f0fadbb4a15ef013ff6ce24 Before we couldn't pass a window for dialogs since there was no activity to provide it, now curtains gives us this window and we can use the high level api in pxiel copy that does the right thing w.r.t surface insets Reviewed By: passy Differential Revision: D53090570 fbshipit-source-id: 969695198cfa5b848238e734d6ddec29681930e5 --- android/build.gradle | 1 + .../flipper/plugins/uidebugger/core/Snapshot.kt | 17 +++++++++-------- build.gradle | 1 + 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 763ebccef18..70264e3b7ae 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -76,6 +76,7 @@ android { implementation deps.openssl implementation deps.fbjni implementation deps.soloader + implementation deps.curtains implementation deps.jsr305 implementation deps.supportAppCompat implementation deps.supportSqlite diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/Snapshot.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/Snapshot.kt index dc4f6b9fb49..51102e1a27a 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/Snapshot.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/Snapshot.kt @@ -7,7 +7,6 @@ package com.facebook.flipper.plugins.uidebugger.core -import android.app.Activity import android.graphics.Canvas import android.os.Build import android.os.Handler @@ -17,6 +16,7 @@ import android.view.PixelCopy import android.view.View import androidx.annotation.RequiresApi import com.facebook.flipper.plugins.uidebugger.LogTag +import curtains.phoneWindow import kotlin.coroutines.Continuation import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -92,25 +92,26 @@ class PixelCopySnapshotter( return if (view.isHardwareAccelerated) { SnapshotCommon.doSnapshotWithErrorHandling(bitmapPool, view, fallback) { - tryCopyViaActivityWindow(view, it) || tryCopyViaInternalSurface(view, it) + tryCopyViaPhoneWindow(view, it) || tryCopyViaInternalSurface(view, it) } } else { fallback.takeSnapshot(view) } } - private suspend fun tryCopyViaActivityWindow( + /** + * This is the preferred method, passing a window into pixel copy correctly accounts for the + * surface insets, this means dialog fragments are snapshotted correctly + */ + private suspend fun tryCopyViaPhoneWindow( view: View, bitmap: BitmapPool.ReusableBitmap ): Boolean { - val decorViewToActivity: Map = ActivityTracker.decorViewToActivityMap - - val activityForDecorView = decorViewToActivity[view] ?: return false + val window = view.phoneWindow ?: return false return suspendCoroutine { continuation -> - PixelCopy.request( - activityForDecorView.window, bitmap.bitmap, pixelCopyCallback(continuation), handler) + PixelCopy.request(window, bitmap.bitmap, pixelCopyCallback(continuation), handler) } } diff --git a/build.gradle b/build.gradle index a0e016e7470..e1c92cdccc9 100644 --- a/build.gradle +++ b/build.gradle @@ -78,6 +78,7 @@ ext.deps = [ fbjni : "com.facebook.fbjni:fbjni:$FBJNI_VERSION", screenshot : 'com.facebook.testing.screenshot:core:0.15.0', boltsTasks : 'com.parse.bolts:bolts-tasks:1.4.0', + curtains : 'com.squareup.curtains:curtains:1.2.5', // Third-party websocket : 'org.java-websocket:Java-WebSocket:1.5.4', openssl : 'com.android.ndk.thirdparty:openssl:1.1.1l-beta-1',