diff --git a/CHANGELOG.md b/CHANGELOG.md index 165fffd4..995fe2f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Next +## 3.8.3 - 2024-10-25 + +- recording: fix crash when calling view.isVisible ([#201](https://github.com/PostHog/posthog-android/pull/201)) +- recording: change debouncerDelayMs default from 500ms to 1000ms (1s) ([#201](https://github.com/PostHog/posthog-android/pull/201)) + ## 3.8.2 - 2024-10-14 - recording: session replay respect feature flag variants ([#197](https://github.com/PostHog/posthog-android/pull/197)) diff --git a/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayIntegration.kt b/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayIntegration.kt index 70488127..bee459c7 100644 --- a/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayIntegration.kt +++ b/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayIntegration.kt @@ -7,6 +7,7 @@ import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint +import android.graphics.Point import android.graphics.PorterDuff import android.graphics.Rect import android.graphics.RectF @@ -30,9 +31,7 @@ import android.view.MotionEvent import android.view.PixelCopy import android.view.View import android.view.ViewGroup -import android.view.ViewStub import android.view.Window -import android.view.accessibility.AccessibilityNodeInfo import android.webkit.WebView import android.widget.Button import android.widget.CheckBox @@ -460,20 +459,47 @@ public class PostHogReplayIntegration( status.lastSnapshot = wireframe } + /** + * Adapted from https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/view/View.java;l=11620;bpv=0;bpt=1 + */ private fun View.isVisible(): Boolean { - // TODO: also check for getGlobalVisibleRect intersects the display - val visible = isShown && width >= 0 && height >= 0 && this !is ViewStub - - // Between API 16 and API 29, this method may incorrectly return false when magnification - // is enabled. On other versions, a node is considered visible even if it is not on - // the screen because magnification is active. - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { - return visible - } + try { + if (isAttachedToWindow) { + // Attached to invisible window means this view is not visible. + if (windowVisibility != View.VISIBLE) { + return false + } + // An invisible predecessor or one with alpha zero means + // that this view is not visible to the user. + var current: Any? = this + while (current is View) { + val view = current + val transitionAlpha = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) view.transitionAlpha else 1f + // We have attach info so this view is attached and there is no + // need to check whether we reach to ViewRootImpl on the way up. + if (view.alpha <= 0 || transitionAlpha <= 0 || view.visibility != View.VISIBLE) { + return false + } + current = view.parent + } + // Check if the view is entirely covered by its predecessors. + val visibleRect = Rect() + val offset = Point() - val nodeInfo = AccessibilityNodeInfo() - onInitializeAccessibilityNodeInfo(nodeInfo) - return visible && nodeInfo.isVisibleToUser + return getGlobalVisibleRect(visibleRect, offset) + + // TODO: also check for getGlobalVisibleRect intersects the display +// if (boundInView != null) { +// visibleRect.offset(-offset.x, -offset.y) +// return boundInView.intersect(visibleRect) +// } + } + } catch (e: Throwable) { + config.logger.log("Session Replay isVisible failed: $e.") + // if there's an exception, we just return true otherwise we might miss some views + return true + } + return false } private fun Drawable.shouldMaskDrawable(): Boolean { diff --git a/posthog-android/src/main/java/com/posthog/android/replay/PostHogSessionReplayConfig.kt b/posthog-android/src/main/java/com/posthog/android/replay/PostHogSessionReplayConfig.kt index a94ea35f..a6d113b0 100644 --- a/posthog-android/src/main/java/com/posthog/android/replay/PostHogSessionReplayConfig.kt +++ b/posthog-android/src/main/java/com/posthog/android/replay/PostHogSessionReplayConfig.kt @@ -44,8 +44,9 @@ public class PostHogSessionReplayConfig * Deboucer delay used to reduce the number of snapshots captured and reduce performance impact * This is used for capturing the view as a wireframe or screenshot * The lower the number more snapshots will be captured but higher the performance impact - * Defaults to 500ms + * Defaults to 1000ms = 1s + * Ps: it was 500ms by default until version 3.8.2 */ @PostHogExperimental - public var debouncerDelayMs: Long = 500, + public var debouncerDelayMs: Long = 1000, )