Skip to content

Commit

Permalink
RUM-1619 Fix the async image loading logic inside the SR view mappers
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusc83 committed Sep 20, 2023
1 parent 9ef76a9 commit a9b96c3
Show file tree
Hide file tree
Showing 52 changed files with 882 additions and 428 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package com.datadog.android.sessionreplay.material

import android.content.res.ColorStateList
import com.datadog.android.sessionreplay.internal.AsyncJobStatusCallback
import com.datadog.android.sessionreplay.internal.recorder.MappingContext
import com.datadog.android.sessionreplay.internal.recorder.mapper.WireframeMapper
import com.datadog.android.sessionreplay.material.internal.densityNormalized
Expand All @@ -25,7 +26,7 @@ internal open class SliderWireframeMapper(
WireframeMapper<Slider, MobileSegment.Wireframe> {

@Suppress("LongMethod")
override fun map(view: Slider, mappingContext: MappingContext):
override fun map(view: Slider, mappingContext: MappingContext, asyncJobStatusCallback: AsyncJobStatusCallback):
List<MobileSegment.Wireframe> {
val activeTrackId = uniqueIdentifierGenerator
.resolveChildUniqueIdentifier(view, TRACK_ACTIVE_KEY_NAME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package com.datadog.android.sessionreplay.material

import android.widget.TextView
import com.datadog.android.sessionreplay.internal.AsyncJobStatusCallback
import com.datadog.android.sessionreplay.internal.recorder.MappingContext
import com.datadog.android.sessionreplay.internal.recorder.SystemInformation
import com.datadog.android.sessionreplay.internal.recorder.mapper.TextViewMapper
Expand All @@ -27,9 +28,14 @@ internal open class TabWireframeMapper(

override fun map(
view: TabView,
mappingContext: MappingContext
mappingContext: MappingContext,
asyncJobStatusCallback: AsyncJobStatusCallback
): List<MobileSegment.Wireframe> {
val labelWireframes = findAndResolveLabelWireframes(view, mappingContext)
val labelWireframes = findAndResolveLabelWireframes(
view,
mappingContext,
asyncJobStatusCallback
)
if (view.isSelected) {
val selectedTabIndicatorWireframe = resolveTabIndicatorWireframe(
view,
Expand Down Expand Up @@ -80,12 +86,20 @@ internal open class TabWireframeMapper(
)
}

private fun findAndResolveLabelWireframes(view: TabView, mappingContext: MappingContext):
private fun findAndResolveLabelWireframes(
view: TabView,
mappingContext: MappingContext,
asyncJobStatusCallback: AsyncJobStatusCallback
):
List<MobileSegment.Wireframe> {
for (i in 0 until view.childCount) {
val viewChild = view.getChildAt(i) ?: continue
if (TextView::class.java.isAssignableFrom(viewChild::class.java)) {
return textViewMapper.map(viewChild as TextView, mappingContext)
return textViewMapper.map(
viewChild as TextView,
mappingContext,
asyncJobStatusCallback
)
}
}
return emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mock
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

Expand Down Expand Up @@ -79,7 +80,7 @@ internal abstract class BaseTabWireframeMapperTest {
)
)
.thenReturn(fakeTabIndicatorUniqueId)
whenever(mockTextWireframeMapper.map(mockTabLabelView, fakeMappingContext))
whenever(mockTextWireframeMapper.map(eq(mockTabLabelView), eq(fakeMappingContext), any()))
.thenReturn(fakeTextWireframes)
testedTabWireframeMapper = provideTestInstance()
}
Expand Down
18 changes: 11 additions & 7 deletions features/dd-sdk-android-session-replay/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ enum com.datadog.android.sessionreplay.SessionReplayPrivacy
- ALLOW
- MASK
- MASK_USER_INPUT
interface com.datadog.android.sessionreplay.internal.AsyncJobStatusCallback
fun jobStarted()
fun jobFinished()
data class com.datadog.android.sessionreplay.internal.recorder.GlobalBounds
constructor(Long, Long, Long, Long)
data class com.datadog.android.sessionreplay.internal.recorder.MappingContext
Expand All @@ -22,25 +25,26 @@ interface com.datadog.android.sessionreplay.internal.recorder.OptionSelectorDete
fun isOptionSelector(android.view.ViewGroup): Boolean
data class com.datadog.android.sessionreplay.internal.recorder.SystemInformation
constructor(GlobalBounds, Int = Configuration.ORIENTATION_UNDEFINED, Float, String? = null)
abstract class com.datadog.android.sessionreplay.internal.recorder.mapper.BaseWireframeMapper<T: android.view.View, S: com.datadog.android.sessionreplay.model.MobileSegment.Wireframe> : WireframeMapper<T, com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>, com.datadog.android.sessionreplay.internal.AsyncImageProcessingCallback
abstract class com.datadog.android.sessionreplay.internal.recorder.mapper.BaseAsyncBackgroundWireframeMapper<T: android.view.View> : BaseWireframeMapper<T, com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
constructor(com.datadog.android.sessionreplay.utils.StringUtils = StringUtils, com.datadog.android.sessionreplay.utils.ViewUtils = ViewUtils)
override fun map(T, com.datadog.android.sessionreplay.internal.recorder.MappingContext, com.datadog.android.sessionreplay.internal.AsyncJobStatusCallback): List<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
companion object
abstract class com.datadog.android.sessionreplay.internal.recorder.mapper.BaseWireframeMapper<T: android.view.View, S: com.datadog.android.sessionreplay.model.MobileSegment.Wireframe> : WireframeMapper<T, S>
constructor(com.datadog.android.sessionreplay.utils.StringUtils = StringUtils, com.datadog.android.sessionreplay.utils.ViewUtils = ViewUtils)
override fun map(T, com.datadog.android.sessionreplay.internal.recorder.MappingContext): List<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
protected fun resolveViewId(android.view.View): Long
protected fun colorAndAlphaAsStringHexa(Int, Int): String
protected fun resolveViewGlobalBounds(android.view.View, Float): com.datadog.android.sessionreplay.internal.recorder.GlobalBounds
protected fun android.graphics.drawable.Drawable.resolveShapeStyleAndBorder(Float): Pair<com.datadog.android.sessionreplay.model.MobileSegment.ShapeStyle?, com.datadog.android.sessionreplay.model.MobileSegment.ShapeBorder?>?
override fun startProcessingImage()
override fun finishProcessingImage()
companion object
class com.datadog.android.sessionreplay.internal.recorder.mapper.MaskInputTextViewMapper : TextViewMapper
constructor()
class com.datadog.android.sessionreplay.internal.recorder.mapper.MaskTextViewMapper : TextViewMapper
constructor()
open class com.datadog.android.sessionreplay.internal.recorder.mapper.TextViewMapper : BaseWireframeMapper<android.widget.TextView, com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
open class com.datadog.android.sessionreplay.internal.recorder.mapper.TextViewMapper : BaseAsyncBackgroundWireframeMapper<android.widget.TextView>
constructor()
override fun map(android.widget.TextView, com.datadog.android.sessionreplay.internal.recorder.MappingContext): List<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
override fun map(android.widget.TextView, com.datadog.android.sessionreplay.internal.recorder.MappingContext, com.datadog.android.sessionreplay.internal.AsyncJobStatusCallback): List<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
interface com.datadog.android.sessionreplay.internal.recorder.mapper.WireframeMapper<T: android.view.View, S: com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
fun map(T, com.datadog.android.sessionreplay.internal.recorder.MappingContext): List<S>
fun map(T, com.datadog.android.sessionreplay.internal.recorder.MappingContext, com.datadog.android.sessionreplay.internal.AsyncJobStatusCallback = NoOpAsyncJobStatusCallback()): List<S>
object com.datadog.android.sessionreplay.utils.StringUtils
fun formatColorAndAlphaAsHexa(Int, Int): String
object com.datadog.android.sessionreplay.utils.UniqueIdentifierGenerator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public final class com/datadog/android/sessionreplay/SessionReplayPrivacy : java
public static fun values ()[Lcom/datadog/android/sessionreplay/SessionReplayPrivacy;
}

public abstract interface class com/datadog/android/sessionreplay/internal/AsyncJobStatusCallback {
public abstract fun jobFinished ()V
public abstract fun jobStarted ()V
}

public final class com/datadog/android/sessionreplay/internal/recorder/GlobalBounds {
public fun <init> (JJJJ)V
public final fun component1 ()J
Expand Down Expand Up @@ -87,18 +92,26 @@ public final class com/datadog/android/sessionreplay/internal/recorder/SystemInf
public fun toString ()Ljava/lang/String;
}

public abstract class com/datadog/android/sessionreplay/internal/recorder/mapper/BaseWireframeMapper : com/datadog/android/sessionreplay/internal/AsyncImageProcessingCallback, com/datadog/android/sessionreplay/internal/recorder/mapper/WireframeMapper {
public abstract class com/datadog/android/sessionreplay/internal/recorder/mapper/BaseAsyncBackgroundWireframeMapper : com/datadog/android/sessionreplay/internal/recorder/mapper/BaseWireframeMapper {
public static final field Companion Lcom/datadog/android/sessionreplay/internal/recorder/mapper/BaseAsyncBackgroundWireframeMapper$Companion;
public fun <init> ()V
public fun <init> (Lcom/datadog/android/sessionreplay/utils/StringUtils;Lcom/datadog/android/sessionreplay/utils/ViewUtils;)V
public synthetic fun <init> (Lcom/datadog/android/sessionreplay/utils/StringUtils;Lcom/datadog/android/sessionreplay/utils/ViewUtils;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun map (Landroid/view/View;Lcom/datadog/android/sessionreplay/internal/recorder/MappingContext;Lcom/datadog/android/sessionreplay/internal/AsyncJobStatusCallback;)Ljava/util/List;
}

public final class com/datadog/android/sessionreplay/internal/recorder/mapper/BaseAsyncBackgroundWireframeMapper$Companion {
}

public abstract class com/datadog/android/sessionreplay/internal/recorder/mapper/BaseWireframeMapper : com/datadog/android/sessionreplay/internal/recorder/mapper/WireframeMapper {
public static final field Companion Lcom/datadog/android/sessionreplay/internal/recorder/mapper/BaseWireframeMapper$Companion;
public fun <init> ()V
public fun <init> (Lcom/datadog/android/sessionreplay/utils/StringUtils;Lcom/datadog/android/sessionreplay/utils/ViewUtils;)V
public synthetic fun <init> (Lcom/datadog/android/sessionreplay/utils/StringUtils;Lcom/datadog/android/sessionreplay/utils/ViewUtils;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
protected final fun colorAndAlphaAsStringHexa (II)Ljava/lang/String;
public fun finishProcessingImage ()V
public fun map (Landroid/view/View;Lcom/datadog/android/sessionreplay/internal/recorder/MappingContext;)Ljava/util/List;
protected final fun resolveShapeStyleAndBorder (Landroid/graphics/drawable/Drawable;F)Lkotlin/Pair;
protected final fun resolveViewGlobalBounds (Landroid/view/View;F)Lcom/datadog/android/sessionreplay/internal/recorder/GlobalBounds;
protected final fun resolveViewId (Landroid/view/View;)J
public fun startProcessingImage ()V
}

public final class com/datadog/android/sessionreplay/internal/recorder/mapper/BaseWireframeMapper$Companion {
Expand All @@ -112,14 +125,18 @@ public final class com/datadog/android/sessionreplay/internal/recorder/mapper/Ma
public fun <init> ()V
}

public class com/datadog/android/sessionreplay/internal/recorder/mapper/TextViewMapper : com/datadog/android/sessionreplay/internal/recorder/mapper/BaseWireframeMapper {
public class com/datadog/android/sessionreplay/internal/recorder/mapper/TextViewMapper : com/datadog/android/sessionreplay/internal/recorder/mapper/BaseAsyncBackgroundWireframeMapper {
public fun <init> ()V
public synthetic fun map (Landroid/view/View;Lcom/datadog/android/sessionreplay/internal/recorder/MappingContext;)Ljava/util/List;
public fun map (Landroid/widget/TextView;Lcom/datadog/android/sessionreplay/internal/recorder/MappingContext;)Ljava/util/List;
public synthetic fun map (Landroid/view/View;Lcom/datadog/android/sessionreplay/internal/recorder/MappingContext;Lcom/datadog/android/sessionreplay/internal/AsyncJobStatusCallback;)Ljava/util/List;
public fun map (Landroid/widget/TextView;Lcom/datadog/android/sessionreplay/internal/recorder/MappingContext;Lcom/datadog/android/sessionreplay/internal/AsyncJobStatusCallback;)Ljava/util/List;
}

public abstract interface class com/datadog/android/sessionreplay/internal/recorder/mapper/WireframeMapper {
public abstract fun map (Landroid/view/View;Lcom/datadog/android/sessionreplay/internal/recorder/MappingContext;)Ljava/util/List;
public abstract fun map (Landroid/view/View;Lcom/datadog/android/sessionreplay/internal/recorder/MappingContext;Lcom/datadog/android/sessionreplay/internal/AsyncJobStatusCallback;)Ljava/util/List;
}

public final class com/datadog/android/sessionreplay/internal/recorder/mapper/WireframeMapper$DefaultImpls {
public static synthetic fun map$default (Lcom/datadog/android/sessionreplay/internal/recorder/mapper/WireframeMapper;Landroid/view/View;Lcom/datadog/android/sessionreplay/internal/recorder/MappingContext;Lcom/datadog/android/sessionreplay/internal/AsyncJobStatusCallback;ILjava/lang/Object;)Ljava/util/List;
}

public final class com/datadog/android/sessionreplay/model/MobileSegment {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ enum class SessionReplayPrivacy {
ALLOW -> {
imageMapper = ViewScreenshotWireframeMapper(viewWireframeMapper)
textMapper = TextViewMapper(
base64Serializer = base64Serializer,
imageWireframeHelper = imageWireframeHelper,
uniqueIdentifierGenerator = uniqueIdentifierGenerator
)
Expand All @@ -123,7 +122,6 @@ enum class SessionReplayPrivacy {
MASK -> {
imageMapper = ViewScreenshotWireframeMapper(viewWireframeMapper)
textMapper = MaskTextViewMapper(
base64Serializer = base64Serializer,
imageWireframeHelper = imageWireframeHelper,
uniqueIdentifierGenerator = uniqueIdentifierGenerator
)
Expand All @@ -139,7 +137,6 @@ enum class SessionReplayPrivacy {
MASK_USER_INPUT -> {
imageMapper = ViewScreenshotWireframeMapper(viewWireframeMapper)
textMapper = MaskInputTextViewMapper(
base64Serializer = base64Serializer,
imageWireframeHelper = imageWireframeHelper,
uniqueIdentifierGenerator = uniqueIdentifierGenerator
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2016-Present Datadog, Inc.
*/

package com.datadog.android.sessionreplay.internal

/**
* A callback to be notified when an async job starts or finishes.
*/
interface AsyncJobStatusCallback {

/**
* Notifies that an async job has started.
*/
fun jobStarted()

/**
* Notifies that an async job has finished.
*/
fun jobFinished()
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@

package com.datadog.android.sessionreplay.internal

internal interface AsyncImageProcessingCallback {
fun startProcessingImage()
fun finishProcessingImage()
internal class NoOpAsyncJobStatusCallback : AsyncJobStatusCallback {
override fun jobStarted() {
// No-op
}
override fun jobFinished() {
// No-op
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ internal data class RecordedDataQueueRefs(
// this can only be populated after the snapshot has been created
internal var recordedDataQueueItem: SnapshotRecordedDataQueueItem? = null

internal fun incrementPendingImages() {
recordedDataQueueItem?.incrementPendingImages()
internal fun incrementPendingJobs() {
recordedDataQueueItem?.incrementPendingJobs()
}

internal fun decrementPendingImages() {
recordedDataQueueItem?.decrementPendingImages()
internal fun decrementPendingJobs() {
recordedDataQueueItem?.decrementPendingJobs()
}

internal fun tryToConsumeItem() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ internal class SnapshotRecordedDataQueueItem(
internal val systemInformation: SystemInformation
) : RecordedDataQueueItem(recordedQueuedItemContext) {
internal var nodes = emptyList<Node>()
internal var pendingImages = AtomicInteger(0)
internal var pendingJobs = AtomicInteger(0)

override fun isValid(): Boolean {
return nodes.isNotEmpty()
}

override fun isReady(): Boolean {
return pendingImages.get() == 0
return pendingJobs.get() == 0
}

internal fun incrementPendingImages() = pendingImages.incrementAndGet()
internal fun decrementPendingImages() = pendingImages.decrementAndGet()
internal fun incrementPendingJobs() = pendingJobs.incrementAndGet()
internal fun decrementPendingJobs() = pendingJobs.decrementAndGet()
}
Loading

0 comments on commit a9b96c3

Please sign in to comment.