diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/StubFragment.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/StubFragment.kt deleted file mode 100644 index c32f66d0e5..0000000000 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/StubFragment.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.simprints.feature.dashboard.settings.troubleshooting - -import androidx.fragment.app.Fragment -import com.simprints.feature.dashboard.R - -// TODO remove once few tab fragments are implemented -class StubFragment : Fragment(R.layout.fragment_troubleshooting_stub) diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/TroubleshootingPagerAdapter.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/TroubleshootingPagerAdapter.kt index a6d2647d29..186ba46158 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/TroubleshootingPagerAdapter.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/TroubleshootingPagerAdapter.kt @@ -3,6 +3,7 @@ package com.simprints.feature.dashboard.settings.troubleshooting import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter +import com.simprints.feature.dashboard.settings.troubleshooting.events.EventScopeLogFragment import com.simprints.feature.dashboard.settings.troubleshooting.overview.OverviewFragment import com.simprints.infra.uibase.annotations.ExcludedFromGeneratedTestCoverageReports @@ -20,10 +21,7 @@ internal class TroubleshootingPagerAdapter( val factory: () -> Fragment, ) { - // TODO Replace stub fragments with proper ones Overview("Overview", { OverviewFragment() }), - Events("Events", { StubFragment() }), - Workers("Worker log", { StubFragment() }), - ; + Events("Event scopes", { EventScopeLogFragment() }), } } diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/adapter/TroubleshootingItemViewData.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/adapter/TroubleshootingItemViewData.kt new file mode 100644 index 0000000000..6ea78b8b4f --- /dev/null +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/adapter/TroubleshootingItemViewData.kt @@ -0,0 +1,8 @@ +package com.simprints.feature.dashboard.settings.troubleshooting.adapter + +data class TroubleshootingItemViewData( + val title: String, + val subtitle: String = "", + val body: String = "", + val navigationId: String? = null, +) diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/adapter/TroubleshootingListAdapter.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/adapter/TroubleshootingListAdapter.kt new file mode 100644 index 0000000000..5a70cf8770 --- /dev/null +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/adapter/TroubleshootingListAdapter.kt @@ -0,0 +1,53 @@ +package com.simprints.feature.dashboard.settings.troubleshooting.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import com.simprints.core.ExcludedFromGeneratedTestCoverageReports +import com.simprints.feature.dashboard.databinding.ItemTroubleshootingListBinding +import com.simprints.infra.uibase.system.Clipboard + + +@ExcludedFromGeneratedTestCoverageReports("UI classes are not unit tested") +internal class TroubleshootingListAdapter( + private val items: List, + private val onMoreClick: (String) -> Unit = {}, +) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val layoutInflater = LayoutInflater.from(parent.context) + val binding = ItemTroubleshootingListBinding.inflate(layoutInflater, parent, false) + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount(): Int = items.size + + @ExcludedFromGeneratedTestCoverageReports("UI classes are not unit tested") + inner class ViewHolder( + private val binding: ItemTroubleshootingListBinding + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(item: TroubleshootingItemViewData) { + binding.troubleshootingItemTitle.text = item.title + binding.troubleshootingItemSubtitle.text = item.subtitle + binding.troubleshootingItemBody.text = item.body + + binding.troubleshootingItemButton.isVisible = item.navigationId != null + binding.troubleshootingItemButton.setOnClickListener { + item.navigationId?.let(onMoreClick) + } + + binding.troubleshootingItemCopy.setOnClickListener { + val context = binding.root.context + Clipboard.copyToClipboard(context, "${item.title}\n${item.subtitle}\n${item.body}") + Toast.makeText(context, "Copied to clipboard", Toast.LENGTH_SHORT).show() + } + } + } +} diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/events/EventLogFragment.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/events/EventLogFragment.kt new file mode 100644 index 0000000000..d8299e444d --- /dev/null +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/events/EventLogFragment.kt @@ -0,0 +1,38 @@ +package com.simprints.feature.dashboard.settings.troubleshooting.events + +import android.os.Bundle +import android.view.View +import androidx.core.view.isGone +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.simprints.feature.dashboard.R +import com.simprints.feature.dashboard.databinding.FragmentTroubleshootingStandaloneListBinding +import com.simprints.feature.dashboard.settings.troubleshooting.adapter.TroubleshootingListAdapter +import com.simprints.infra.uibase.viewbinding.viewBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +internal class EventLogFragment : Fragment(R.layout.fragment_troubleshooting_standalone_list) { + + private val args by navArgs() + + private val viewModel by viewModels() + private val binding by viewBinding(FragmentTroubleshootingStandaloneListBinding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.troubleshootingToolbar.title = args.scopeId + binding.troubleshootingToolbar.setNavigationOnClickListener { + findNavController().navigateUp() + } + + viewModel.events.observe(viewLifecycleOwner) { + binding.troubleshootingListProgress.isGone = it.isNotEmpty() + binding.troubleshootingList.adapter = TroubleshootingListAdapter(it) + } + viewModel.collectEvents(args.scopeId) + } +} diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/events/EventScopeLogFragment.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/events/EventScopeLogFragment.kt new file mode 100644 index 0000000000..917b863187 --- /dev/null +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/events/EventScopeLogFragment.kt @@ -0,0 +1,39 @@ +package com.simprints.feature.dashboard.settings.troubleshooting.events + +import android.os.Bundle +import android.view.View +import androidx.core.view.isGone +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.simprints.feature.dashboard.R +import com.simprints.feature.dashboard.databinding.FragmentTroubleshootingListBinding +import com.simprints.feature.dashboard.settings.troubleshooting.TroubleshootingFragmentDirections +import com.simprints.feature.dashboard.settings.troubleshooting.adapter.TroubleshootingListAdapter +import com.simprints.infra.uibase.viewbinding.viewBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +internal class EventScopeLogFragment : Fragment(R.layout.fragment_troubleshooting_list) { + + private val viewModel by viewModels() + private val binding by viewBinding(FragmentTroubleshootingListBinding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.scopes.observe(viewLifecycleOwner) { + binding.troubleshootingListProgress.isGone = it.isNotEmpty() + binding.troubleshootingList.adapter = + TroubleshootingListAdapter(it) { openEventsList(it) } + } + viewModel.collectEventScopes() + } + + private fun openEventsList(scopeId: String) { + findNavController().navigate( + TroubleshootingFragmentDirections + .actionTroubleshootingFragmentToTroubleshootingEventLogFragment(scopeId) + ) + } +} diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/events/EventsLogViewModel.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/events/EventsLogViewModel.kt new file mode 100644 index 0000000000..af602dc976 --- /dev/null +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/troubleshooting/events/EventsLogViewModel.kt @@ -0,0 +1,73 @@ +package com.simprints.feature.dashboard.settings.troubleshooting.events + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.simprints.feature.dashboard.settings.troubleshooting.adapter.TroubleshootingItemViewData +import com.simprints.infra.events.EventRepository +import com.simprints.infra.events.event.domain.models.Event +import com.simprints.infra.events.event.domain.models.scope.EventScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import java.util.Date +import javax.inject.Inject + +@HiltViewModel +internal class EventsLogViewModel @Inject constructor( + private val eventRepository: EventRepository, +) : ViewModel() { + + private val _scopes = MutableLiveData>(emptyList()) + val scopes: LiveData> + get() = _scopes + + private val _events = MutableLiveData>(emptyList()) + val events: LiveData> + get() = _events + + fun collectEventScopes() { + viewModelScope.launch { + eventRepository.getAllScopes() + .map { scope -> formatScopeViewData(scope) } + .ifEmpty { listOf(TroubleshootingItemViewData(title = "No event scopes found")) } + .let { _scopes.postValue(it) } + } + } + + fun collectEvents(scopeId: String) { + viewModelScope.launch { + eventRepository.getEventsFromScope(scopeId) + .map { event -> formatEventViewData(event) } + .reversed() + .ifEmpty { listOf(TroubleshootingItemViewData(title = "No events found")) } + .let { _events.postValue(it) } + } + } + + private fun formatScopeViewData(scope: EventScope): TroubleshootingItemViewData = + TroubleshootingItemViewData( + title = scope.id, + subtitle = formatTimestampSubtitle(scope.createdAt.ms, scope.endedAt?.ms), + body = """ + Type: ${scope.type} | End cause: ${scope.payload.endCause} + ${scope.payload.language} | ${scope.payload.sidVersion} | Lib: ${scope.payload.libSimprintsVersion} + Configuration ID: ${scope.payload.projectConfigurationId} + """.trimIndent(), + navigationId = scope.id, + ) + + private fun formatEventViewData(event: Event): TroubleshootingItemViewData = + TroubleshootingItemViewData( + title = event.type.name, + subtitle = formatTimestampSubtitle( + event.payload.createdAt.ms, + event.payload.endedAt?.ms + ), + body = "ID: ${event.id}\n" + event.payload.toSafeString(), + ) + + private fun formatTimestampSubtitle(startMs: Long, endMs: Long? = null): String = + "Started: ${Date(startMs)}" + endMs?.let { "\nEnded: ${Date(it)}" }.orEmpty() + +} diff --git a/feature/dashboard/src/main/res/layout/fragment_troubleshooting_list.xml b/feature/dashboard/src/main/res/layout/fragment_troubleshooting_list.xml new file mode 100644 index 0000000000..3a04211d8d --- /dev/null +++ b/feature/dashboard/src/main/res/layout/fragment_troubleshooting_list.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/feature/dashboard/src/main/res/layout/fragment_troubleshooting_standalone_list.xml b/feature/dashboard/src/main/res/layout/fragment_troubleshooting_standalone_list.xml new file mode 100644 index 0000000000..897bcbd6a3 --- /dev/null +++ b/feature/dashboard/src/main/res/layout/fragment_troubleshooting_standalone_list.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + diff --git a/feature/dashboard/src/main/res/layout/item_troubleshooting_list.xml b/feature/dashboard/src/main/res/layout/item_troubleshooting_list.xml new file mode 100644 index 0000000000..792d081d72 --- /dev/null +++ b/feature/dashboard/src/main/res/layout/item_troubleshooting_list.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + diff --git a/feature/dashboard/src/main/res/navigation/graph_dashboard.xml b/feature/dashboard/src/main/res/navigation/graph_dashboard.xml index dd6f95bee3..dfad2fd3de 100644 --- a/feature/dashboard/src/main/res/navigation/graph_dashboard.xml +++ b/feature/dashboard/src/main/res/navigation/graph_dashboard.xml @@ -25,8 +25,7 @@ app:popUpToInclusive="true" /> + app:destination="@id/troubleshootingFragment" /> + tools:layout="@layout/fragment_troubleshooting"> + + + + + + suspend fun getOpenEventScopes(type: EventScopeType): List suspend fun getClosedEventScopes(type: EventScopeType, limit: Int): List suspend fun getClosedEventScopesCount(type: EventScopeType): Int diff --git a/infra/events/src/main/java/com/simprints/infra/events/EventRepositoryImpl.kt b/infra/events/src/main/java/com/simprints/infra/events/EventRepositoryImpl.kt index d5fad0577a..0aaf15caa2 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/EventRepositoryImpl.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/EventRepositoryImpl.kt @@ -123,6 +123,8 @@ internal open class EventRepositoryImpl @Inject constructor( override suspend fun saveEventScope(eventScope: EventScope) { eventLocalDataSource.saveEventScope(eventScope) } + override suspend fun getAllScopes(): List = + eventLocalDataSource.loadAllScopes() override suspend fun getOpenEventScopes(type: EventScopeType): List = eventLocalDataSource.loadOpenedScopes(type) diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AgeGroupSelectionEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AgeGroupSelectionEvent.kt index 41de698c1a..978dba2fb2 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AgeGroupSelectionEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AgeGroupSelectionEvent.kt @@ -38,7 +38,10 @@ data class AgeGroupSelectionEvent( override val endedAt: Timestamp?, val subjectAgeGroup: AgeGroup, override val type: EventType = AGE_GROUP_SELECTION, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "age group: [${subjectAgeGroup.startInclusive}, ${subjectAgeGroup.endExclusive})" + } @Keep data class AgeGroup( diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AlertScreenEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AlertScreenEvent.kt index 7811f2c75a..ef55ce083c 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AlertScreenEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AlertScreenEvent.kt @@ -39,6 +39,8 @@ data class AlertScreenEvent( override val type: EventType = ALERT_SCREEN, ) : EventPayload() { + override fun toSafeString(): String = "type: $alertType" + enum class AlertScreenEventType { DIFFERENT_PROJECT_ID, DIFFERENT_USER_ID, diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AuthenticationEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AuthenticationEvent.kt index 9df087498a..eb643994ea 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AuthenticationEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AuthenticationEvent.kt @@ -50,6 +50,8 @@ data class AuthenticationEvent( override val type: EventType = AUTHENTICATION, ) : EventPayload() { + override fun toSafeString(): String = "result: $result" + @Keep data class UserInfo(val projectId: String, val userId: TokenizableString) diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AuthorizationEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AuthorizationEvent.kt index 40e741232d..5899bfff17 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AuthorizationEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/AuthorizationEvent.kt @@ -50,6 +50,8 @@ data class AuthorizationEvent( override val type: EventType = AUTHORIZATION, ) : EventPayload() { + override fun toSafeString(): String = "result: $result" + @Keep enum class AuthorizationResult { AUTHORIZED, NOT_AUTHORIZED diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/CandidateReadEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/CandidateReadEvent.kt index 4b6cc78475..149591d0f5 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/CandidateReadEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/CandidateReadEvent.kt @@ -50,6 +50,9 @@ data class CandidateReadEvent( override val type: EventType = CANDIDATE_READ ) : EventPayload() { + override fun toSafeString(): String = + "guid: $candidateId, local: $localResult, remote: $remoteResult" + @Keep enum class LocalResult { FOUND, NOT_FOUND diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/CompletionCheckEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/CompletionCheckEvent.kt index a100db8959..d03b773adb 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/CompletionCheckEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/CompletionCheckEvent.kt @@ -37,7 +37,10 @@ data class CompletionCheckEvent( val completed: Boolean, override val endedAt: Timestamp? = null, override val type: EventType = COMPLETION_CHECK, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "completed: $completed" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ConnectivitySnapshotEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ConnectivitySnapshotEvent.kt index d1628d35d7..27fce92b8e 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ConnectivitySnapshotEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ConnectivitySnapshotEvent.kt @@ -38,7 +38,11 @@ data class ConnectivitySnapshotEvent( val connections: List, override val endedAt: Timestamp? = null, override val type: EventType = CONNECTIVITY_SNAPSHOT, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = + connections.joinToString(", ") { "${it.type}: ${it.state}" } + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ConsentEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ConsentEvent.kt index 71ee7e5f35..2c9b8b03ea 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ConsentEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ConsentEvent.kt @@ -42,6 +42,8 @@ data class ConsentEvent( override val type: EventType = CONSENT, ) : EventPayload() { + override fun toSafeString(): String = "consent: $consentType, result: $result" + @Keep enum class Type { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EnrolmentEventV1.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EnrolmentEventV1.kt index f547c03466..2af5cea913 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EnrolmentEventV1.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EnrolmentEventV1.kt @@ -38,7 +38,10 @@ data class EnrolmentEventV1( val personId: String, override val endedAt: Timestamp? = null, override val type: EventType = ENROLMENT_V1, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "person ID: $personId" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EnrolmentEventV2.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EnrolmentEventV2.kt index 8204e02d7c..379c3df8ce 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EnrolmentEventV2.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EnrolmentEventV2.kt @@ -60,7 +60,10 @@ data class EnrolmentEventV2( val personCreationEventId: String, override val endedAt: Timestamp? = null, override val type: EventType = ENROLMENT_V2, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "subject ID: $subjectId, module ID: $moduleId" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EventPayload.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EventPayload.kt index e516bd2651..518cbff56a 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EventPayload.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/EventPayload.kt @@ -36,13 +36,13 @@ import com.simprints.infra.events.event.domain.models.callout.EnrolmentLastBiome import com.simprints.infra.events.event.domain.models.callout.IdentificationCalloutEvent.IdentificationCalloutPayload import com.simprints.infra.events.event.domain.models.callout.VerificationCalloutEvent.VerificationCalloutPayload import com.simprints.infra.events.event.domain.models.downsync.EventDownSyncRequestEvent.EventDownSyncRequestPayload -import com.simprints.infra.events.event.domain.models.face.FaceCaptureBiometricsEvent +import com.simprints.infra.events.event.domain.models.face.FaceCaptureBiometricsEvent.FaceCaptureBiometricsPayload import com.simprints.infra.events.event.domain.models.face.FaceCaptureConfirmationEvent.FaceCaptureConfirmationPayload -import com.simprints.infra.events.event.domain.models.face.FaceCaptureEvent +import com.simprints.infra.events.event.domain.models.face.FaceCaptureEvent.FaceCapturePayload import com.simprints.infra.events.event.domain.models.face.FaceFallbackCaptureEvent.FaceFallbackCapturePayload import com.simprints.infra.events.event.domain.models.face.FaceOnboardingCompleteEvent.FaceOnboardingCompletePayload -import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureBiometricsEvent -import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureEvent +import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureBiometricsEvent.FingerprintCaptureBiometricsPayload +import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureEvent.FingerprintCapturePayload import com.simprints.infra.events.event.domain.models.upsync.EventUpSyncRequestEvent.EventUpSyncRequestPayload @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true) @@ -59,8 +59,8 @@ import com.simprints.infra.events.event.domain.models.upsync.EventUpSyncRequestE JsonSubTypes.Type(value = IdentificationCalloutPayload::class, name = EventType.CALLOUT_IDENTIFICATION_KEY), JsonSubTypes.Type(value = VerificationCalloutPayload::class, name = EventType.CALLOUT_VERIFICATION_KEY), JsonSubTypes.Type(value = FaceCaptureConfirmationPayload::class, name = EventType.FACE_CAPTURE_CONFIRMATION_KEY), - JsonSubTypes.Type(value = FaceCaptureEvent.FaceCapturePayload::class, name = EventType.FACE_CAPTURE_KEY), - JsonSubTypes.Type(value = FaceCaptureBiometricsEvent.FaceCaptureBiometricsPayload::class, name = EventType.FACE_CAPTURE_BIOMETRICS_KEY), + JsonSubTypes.Type(value = FaceCapturePayload::class, name = EventType.FACE_CAPTURE_KEY), + JsonSubTypes.Type(value = FaceCaptureBiometricsPayload::class, name = EventType.FACE_CAPTURE_BIOMETRICS_KEY), JsonSubTypes.Type(value = FaceFallbackCapturePayload::class, name = EventType.FACE_FALLBACK_CAPTURE_KEY), JsonSubTypes.Type(value = FaceOnboardingCompletePayload::class, name = EventType.FACE_ONBOARDING_COMPLETE_KEY), JsonSubTypes.Type(value = AlertScreenPayload::class, name = EventType.ALERT_SCREEN_KEY), @@ -72,8 +72,8 @@ import com.simprints.infra.events.event.domain.models.upsync.EventUpSyncRequestE JsonSubTypes.Type(value = ConsentPayload::class, name = EventType.CONSENT_KEY), JsonSubTypes.Type(value = EnrolmentEventV1.EnrolmentPayload::class, name = EventType.ENROLMENT_V1_KEY), JsonSubTypes.Type(value = EnrolmentEventV2.EnrolmentPayload::class, name = EventType.ENROLMENT_V2_KEY), - JsonSubTypes.Type(value = FingerprintCaptureEvent.FingerprintCapturePayload::class, name = EventType.FINGERPRINT_CAPTURE_KEY), - JsonSubTypes.Type(value = FingerprintCaptureBiometricsEvent.FingerprintCaptureBiometricsPayload::class, name = EventType.FINGERPRINT_CAPTURE_BIOMETRICS_KEY), + JsonSubTypes.Type(value = FingerprintCapturePayload::class, name = EventType.FINGERPRINT_CAPTURE_KEY), + JsonSubTypes.Type(value = FingerprintCaptureBiometricsPayload::class, name = EventType.FINGERPRINT_CAPTURE_BIOMETRICS_KEY), JsonSubTypes.Type(value = GuidSelectionPayload::class, name = EventType.GUID_SELECTION_KEY), JsonSubTypes.Type(value = IntentParsingPayload::class, name = EventType.INTENT_PARSING_KEY), JsonSubTypes.Type(value = InvalidIntentPayload::class, name = EventType.INVALID_INTENT_KEY), @@ -95,4 +95,6 @@ abstract class EventPayload { abstract val eventVersion: Int abstract val createdAt: Timestamp abstract val endedAt: Timestamp? + + open fun toSafeString(): String = "" } diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/GuidSelectionEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/GuidSelectionEvent.kt index ffdb04b2dc..713e6d6c89 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/GuidSelectionEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/GuidSelectionEvent.kt @@ -36,7 +36,10 @@ data class GuidSelectionEvent( val selectedId: String, override val endedAt: Timestamp? = null, override val type: EventType = GUID_SELECTION, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "guid: $selectedId" + } companion object { const val EVENT_VERSION = 2 diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/IntentParsingEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/IntentParsingEvent.kt index 09ca79edaf..a449b03b58 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/IntentParsingEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/IntentParsingEvent.kt @@ -40,6 +40,8 @@ data class IntentParsingEvent( override val type: EventType = INTENT_PARSING, ) : EventPayload() { + override fun toSafeString(): String = "integration: $integration" + @Keep enum class IntegrationInfo { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/InvalidIntentEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/InvalidIntentEvent.kt index aae6a1dee2..802dee9b0e 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/InvalidIntentEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/InvalidIntentEvent.kt @@ -39,7 +39,12 @@ data class InvalidIntentEvent( val extras: Map, override val endedAt: Timestamp? = null, override val type: EventType = INVALID_INTENT, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String { + return "action: $action, extras: $extras" + } + } companion object { const val EVENT_VERSION = 2 diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/LicenseCheckEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/LicenseCheckEvent.kt index 58a0aa6310..af39cce24c 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/LicenseCheckEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/LicenseCheckEvent.kt @@ -51,7 +51,11 @@ constructor( val vendor: String, override val endedAt: Timestamp? = null, override val type: EventType = LICENSE_CHECK, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "vendor: $vendor, status: $status" + } + companion object { const val EVENT_VERSION = 1 } diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/OneToManyMatchEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/OneToManyMatchEvent.kt index cbd5df9035..2392e97284 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/OneToManyMatchEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/OneToManyMatchEvent.kt @@ -44,6 +44,9 @@ data class OneToManyMatchEvent( override val type: EventType = ONE_TO_MANY_MATCH, ) : EventPayload() { + override fun toSafeString(): String = + "matcher: $matcher, pool: ${pool.type}, size: ${pool.count}, results: ${result?.size}" + @Keep data class MatchPool(val type: MatchPoolType, val count: Int) diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/OneToOneMatchEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/OneToOneMatchEvent.kt index 7f7aef299d..730a15fa70 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/OneToOneMatchEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/OneToOneMatchEvent.kt @@ -52,7 +52,12 @@ data class OneToOneMatchEvent( val result: MatchEntry?, val fingerComparisonStrategy: FingerComparisonStrategy?, override val type: EventType = ONE_TO_ONE_MATCH, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = + "matcher: $matcher, candidate ID: $candidateId, " + + "result: ${result?.score}, finger strategy: $fingerComparisonStrategy" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/PersonCreationEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/PersonCreationEvent.kt index 370824a747..cb3bc66758 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/PersonCreationEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/PersonCreationEvent.kt @@ -51,7 +51,11 @@ data class PersonCreationEvent( val faceReferenceId: String?, override val endedAt: Timestamp? = null, override val type: EventType = PERSON_CREATION, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = + "face reference: $faceReferenceId, fingerprint reference: $fingerprintReferenceId" + } fun hasFingerprintReference() = payload.fingerprintReferenceId != null fun hasFaceReference() = payload.faceReferenceId != null diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/RefusalEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/RefusalEvent.kt index f23079ff69..95496ae400 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/RefusalEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/RefusalEvent.kt @@ -49,6 +49,8 @@ data class RefusalEvent( override val type: EventType = REFUSAL, ) : EventPayload() { + override fun toSafeString(): String = "reason: $reason, extra: $otherText" + @Keep enum class Answer { REFUSED_RELIGION, diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ScannerConnectionEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ScannerConnectionEvent.kt index 15733222dd..be42b65c26 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ScannerConnectionEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ScannerConnectionEvent.kt @@ -39,6 +39,10 @@ data class ScannerConnectionEvent( override val type: EventType = SCANNER_CONNECTION, ) : EventPayload() { + override fun toSafeString(): String = + "scanner: ${scannerInfo.scannerId}, mac: ${scannerInfo.macAddress}, " + + "generation: ${scannerInfo.generation}, hardware version: ${scannerInfo.hardwareVersion}" + @Keep data class ScannerInfo( val scannerId: String, diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ScannerFirmwareUpdateEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ScannerFirmwareUpdateEvent.kt index 4377fd429e..db2ca1e292 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ScannerFirmwareUpdateEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/ScannerFirmwareUpdateEvent.kt @@ -50,7 +50,11 @@ data class ScannerFirmwareUpdateEvent( val targetAppVersion: String, var failureReason: String? = null, override val type: EventType = SCANNER_FIRMWARE_UPDATE, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = + "chip: $chip, target version: $targetAppVersion, failure reason: $failureReason" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/SuspiciousIntentEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/SuspiciousIntentEvent.kt index 98bd89be6d..59a7fabf30 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/SuspiciousIntentEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/SuspiciousIntentEvent.kt @@ -37,7 +37,11 @@ data class SuspiciousIntentEvent( val unexpectedExtras: Map, override val endedAt: Timestamp? = null, override val type: EventType = SUSPICIOUS_INTENT, - ) : EventPayload() + ) : EventPayload() { + override fun toSafeString(): String = unexpectedExtras.entries.joinToString(", ") { + "${it.key}: ${it.value}" + } + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/Vero2InfoSnapshotEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/Vero2InfoSnapshotEvent.kt index 39937af7be..4a891577aa 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/Vero2InfoSnapshotEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/Vero2InfoSnapshotEvent.kt @@ -36,7 +36,8 @@ data class Vero2InfoSnapshotEvent( override fun getTokenizedFields(): Map = emptyMap() - override fun setTokenizedFields(map: Map) = this // No tokenized fields + override fun setTokenizedFields(map: Map) = + this // No tokenized fields @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -64,6 +65,11 @@ data class Vero2InfoSnapshotEvent( override val type: EventType = VERO_2_INFO_SNAPSHOT, ) : EventPayload() { + override fun toSafeString(): String = "battery charge: ${battery.charge}, " + + version.let { it as? Vero2Version.Vero2NewApiVersion }?.let { + "hardware: ${it.hardwareRevision}, cypress: ${it.cypressApp}, stm: ${it.stmApp}, un20: ${it.un20App}" + }.orEmpty() + @Keep data class Vero2InfoSnapshotPayloadForNewApi( override val createdAt: Timestamp, diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/ConfirmationCallbackEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/ConfirmationCallbackEvent.kt index bc679cbc8a..ff7a405fe0 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/ConfirmationCallbackEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/ConfirmationCallbackEvent.kt @@ -40,7 +40,10 @@ data class ConfirmationCallbackEvent( val identificationOutcome: Boolean, override val endedAt: Timestamp? = null, override val type: EventType = CALLBACK_CONFIRMATION, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "outcome: $identificationOutcome" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/EnrolmentCallbackEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/EnrolmentCallbackEvent.kt index 446ade622d..73cb40e4da 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/EnrolmentCallbackEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/EnrolmentCallbackEvent.kt @@ -40,7 +40,10 @@ data class EnrolmentCallbackEvent( val guid: String, override val endedAt: Timestamp? = null, override val type: EventType = CALLBACK_ENROLMENT, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "guid: $guid" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/ErrorCallbackEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/ErrorCallbackEvent.kt index 3d87d08fff..489e6dbdca 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/ErrorCallbackEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/ErrorCallbackEvent.kt @@ -43,6 +43,8 @@ data class ErrorCallbackEvent( override val type: EventType = CALLBACK_ERROR, ) : EventPayload() { + override fun toSafeString(): String = "reason: $reason" + @Keep enum class Reason { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/IdentificationCallbackEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/IdentificationCallbackEvent.kt index cab5c537c6..cda73adef6 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/IdentificationCallbackEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/IdentificationCallbackEvent.kt @@ -41,7 +41,13 @@ data class IdentificationCallbackEvent( val scores: List, override val endedAt: Timestamp? = null, override val type: EventType = EventType.CALLBACK_IDENTIFICATION, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = + scores.joinToString(", ", prefix = "[", postfix = "]") { + "${it.guid}: ${it.confidence}" + } + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/RefusalCallbackEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/RefusalCallbackEvent.kt index b7a9900ace..aa68c7406e 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/RefusalCallbackEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/RefusalCallbackEvent.kt @@ -42,7 +42,10 @@ data class RefusalCallbackEvent( val extra: String, override val endedAt: Timestamp? = null, override val type: EventType = CALLBACK_REFUSAL, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "reason: $reason, extra: $extra" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/VerificationCallbackEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/VerificationCallbackEvent.kt index a300b02887..49ba12fd0d 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/VerificationCallbackEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callback/VerificationCallbackEvent.kt @@ -40,7 +40,10 @@ data class VerificationCallbackEvent( val score: CallbackComparisonScore, override val endedAt: Timestamp? = null, override val type: EventType = CALLBACK_VERIFICATION, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "confidence: ${score.confidence}" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/ConfirmationCalloutEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/ConfirmationCalloutEvent.kt index c34a02a7c1..1f3f6b6663 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/ConfirmationCalloutEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/ConfirmationCalloutEvent.kt @@ -44,7 +44,10 @@ data class ConfirmationCalloutEvent( val sessionId: String, override val endedAt: Timestamp? = null, override val type: EventType = CALLOUT_CONFIRMATION, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "guid: $selectedGuid, session ID: $sessionId" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/EnrolmentCalloutEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/EnrolmentCalloutEvent.kt index 88d5a9a0c2..077af564fa 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/EnrolmentCalloutEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/EnrolmentCalloutEvent.kt @@ -61,7 +61,10 @@ data class EnrolmentCalloutEvent( val metadata: String?, override val endedAt: Timestamp? = null, override val type: EventType = CALLOUT_ENROLMENT, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "module: $moduleId, metadata: $metadata" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/EnrolmentLastBiometricsCalloutEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/EnrolmentLastBiometricsCalloutEvent.kt index 812311d73e..2e4b3522f8 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/EnrolmentLastBiometricsCalloutEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/EnrolmentLastBiometricsCalloutEvent.kt @@ -63,7 +63,10 @@ data class EnrolmentLastBiometricsCalloutEvent( val sessionId: String, override val endedAt: Timestamp? = null, override val type: EventType = CALLOUT_LAST_BIOMETRICS, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "metadata: $metadata, session ID: $sessionId" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/IdentificationCalloutEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/IdentificationCalloutEvent.kt index 5a4184f744..a4318e1aa2 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/IdentificationCalloutEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/IdentificationCalloutEvent.kt @@ -60,7 +60,10 @@ data class IdentificationCalloutEvent( val metadata: String?, override val endedAt: Timestamp? = null, override val type: EventType = CALLOUT_IDENTIFICATION, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = "module ID: $moduleId, metadata: $metadata" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/VerificationCalloutEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/VerificationCalloutEvent.kt index 51dabd5ee9..1d77817b7c 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/VerificationCalloutEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/callout/VerificationCalloutEvent.kt @@ -63,7 +63,11 @@ data class VerificationCalloutEvent( val metadata: String, override val endedAt: Timestamp? = null, override val type: EventType = CALLOUT_VERIFICATION, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = + "module ID: $moduleId, guid: $verifyGuid, metadata: $metadata" + } companion object { diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/downsync/EventDownSyncRequestEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/downsync/EventDownSyncRequestEvent.kt index f0ad624974..ef50107809 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/downsync/EventDownSyncRequestEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/downsync/EventDownSyncRequestEvent.kt @@ -61,7 +61,12 @@ data class EventDownSyncRequestEvent( val eventsRead: Int?, override val eventVersion: Int, override val type: EventType = EventType.EVENT_DOWN_SYNC_REQUEST, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = + "request ID: $requestId, status: $responseStatus, error: $errorType, " + + "ms to response: $msToFirstResponseByte, events read: $eventsRead" + } @Keep data class QueryParameters( diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureBiometricsEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureBiometricsEvent.kt index d6c046283e..0c5a77acda 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureBiometricsEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureBiometricsEvent.kt @@ -49,6 +49,8 @@ data class FaceCaptureBiometricsEvent( override val type: EventType = EventType.FACE_CAPTURE_BIOMETRICS, ) : EventPayload() { + override fun toSafeString(): String = "format: ${face.format}, quality: ${face.quality}" + @Keep data class Face( val yaw: Float, diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureConfirmationEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureConfirmationEvent.kt index fd7d289209..15d18f7a0d 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureConfirmationEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureConfirmationEvent.kt @@ -44,6 +44,8 @@ data class FaceCaptureConfirmationEvent( override val type: EventType = FACE_CAPTURE_CONFIRMATION, ) : EventPayload() { + override fun toSafeString(): String = "result: $result" + enum class Result { CONTINUE, RECAPTURE diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureEvent.kt index efe048d7c1..88b5de0ea8 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/face/FaceCaptureEvent.kt @@ -65,6 +65,10 @@ data class FaceCaptureEvent( override val type: EventType = FACE_CAPTURE, ) : EventPayload() { + override fun toSafeString(): String = + "result: $result, attempt nr: $attemptNb, fallback: $isFallback, " + + "quality: ${face?.quality}, format: ${face?.format}" + @Keep data class Face( val yaw: Float, diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEvent.kt index 7d5ef8bfcc..8e1a21bc59 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureBiometricsEvent.kt @@ -50,6 +50,9 @@ data class FingerprintCaptureBiometricsEvent( override val type: EventType = EventType.FINGERPRINT_CAPTURE_BIOMETRICS, ) : EventPayload() { + override fun toSafeString(): String = + "format: ${fingerprint.format}, quality: ${fingerprint.quality}" + @Keep data class Fingerprint( val finger: IFingerIdentifier, diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEvent.kt index 4b42323068..7cee736e07 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/fingerprint/FingerprintCaptureEvent.kt @@ -62,6 +62,10 @@ data class FingerprintCaptureEvent( override val type: EventType = FINGERPRINT_CAPTURE, ) : EventPayload() { + override fun toSafeString(): String = + "finger: ${finger}, result: $result, " + + "quality: ${fingerprint?.quality}, format: ${fingerprint?.format}" + @Keep data class Fingerprint( val finger: IFingerIdentifier, diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/upsync/EventUpSyncRequestEvent.kt b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/upsync/EventUpSyncRequestEvent.kt index 413072cb9a..2f0593ff62 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/upsync/EventUpSyncRequestEvent.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/domain/models/upsync/EventUpSyncRequestEvent.kt @@ -49,7 +49,12 @@ data class EventUpSyncRequestEvent( val errorType: String?, override val eventVersion: Int, override val type: EventType = EventType.EVENT_UP_SYNC_REQUEST, - ) : EventPayload() + ) : EventPayload() { + + override fun toSafeString(): String = + "request ID: $requestId, response: $responseStatus, error: $errorType," + + "sessions: ${content.sessionCount}, eventsUp: ${content.eventUpSyncCount}, eventsDown: ${content.eventDownSyncCount}" + } @Keep data class UpSyncContent( diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/local/EventLocalDataSource.kt b/infra/events/src/main/java/com/simprints/infra/events/event/local/EventLocalDataSource.kt index 85361a932d..e1a55803a8 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/local/EventLocalDataSource.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/local/EventLocalDataSource.kt @@ -104,6 +104,10 @@ internal open class EventLocalDataSource @Inject constructor( scopeDao.countClosed(type) } + suspend fun loadAllScopes(): List = useRoom(readingDispatcher) { + scopeDao.loadAll().map { it.fromDbToDomain(jsonHelper) } + } + suspend fun loadOpenedScopes(type: EventScopeType): List = useRoom(readingDispatcher) { scopeDao.loadOpen(type).map { it.fromDbToDomain(jsonHelper) } diff --git a/infra/events/src/main/java/com/simprints/infra/events/event/local/SessionScopeRoomDao.kt b/infra/events/src/main/java/com/simprints/infra/events/event/local/SessionScopeRoomDao.kt index 8b332bac99..8b37158ca9 100644 --- a/infra/events/src/main/java/com/simprints/infra/events/event/local/SessionScopeRoomDao.kt +++ b/infra/events/src/main/java/com/simprints/infra/events/event/local/SessionScopeRoomDao.kt @@ -11,6 +11,9 @@ import com.simprints.infra.events.event.local.models.DbEventScope @Dao internal interface SessionScopeRoomDao { + @Query("select * from DbEventScope order by start_unixMs desc") + suspend fun loadAll(): List + @Query("select * from DbEventScope where type = :type AND end_unixMs IS NULL order by start_unixMs desc") suspend fun loadOpen(type: EventScopeType): List diff --git a/infra/events/src/test/java/com/simprints/infra/events/EventRepositoryImplTest.kt b/infra/events/src/test/java/com/simprints/infra/events/EventRepositoryImplTest.kt index 50509f47ca..052ffb02d3 100644 --- a/infra/events/src/test/java/com/simprints/infra/events/EventRepositoryImplTest.kt +++ b/infra/events/src/test/java/com/simprints/infra/events/EventRepositoryImplTest.kt @@ -242,6 +242,13 @@ internal class EventRepositoryImplTest { coVerify { eventLocalDataSource.saveEventScope(any()) } } + @Test + fun `should delegate all scope fetch`() = runTest { + eventRepo.getAllScopes() + + coVerify { eventLocalDataSource.loadAllScopes() } + } + @Test fun `should delegate open scope fetch`() = runTest { eventRepo.getOpenEventScopes(type = EventScopeType.SESSION) @@ -256,6 +263,13 @@ internal class EventRepositoryImplTest { coVerify { eventLocalDataSource.loadClosedScopes(EventScopeType.SESSION, limit = 10) } } + @Test + fun `should delegate closed scope count fetch`() = runTest { + eventRepo.getClosedEventScopesCount(type = EventScopeType.SESSION) + + coVerify { eventLocalDataSource.countClosedEventScopes(EventScopeType.SESSION) } + } + @Test fun `deleting scope should delete events in scope`() = runTest { eventRepo.deleteEventScope("scopeId") diff --git a/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/EventPayloadTest.kt b/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/EventPayloadTest.kt new file mode 100644 index 0000000000..7718a8d735 --- /dev/null +++ b/infra/events/src/test/java/com/simprints/infra/events/event/domain/models/EventPayloadTest.kt @@ -0,0 +1,242 @@ +package com.simprints.infra.events.event.domain.models + +import com.google.common.truth.Truth.assertThat +import com.simprints.core.domain.fingerprint.IFingerIdentifier +import com.simprints.core.domain.response.AppMatchConfidence +import com.simprints.core.domain.response.AppResponseTier.TIER_1 +import com.simprints.core.tools.utils.SimNetworkUtils +import com.simprints.core.tools.utils.SimNetworkUtils.Connection +import com.simprints.infra.events.event.domain.models.AlertScreenEvent.AlertScreenPayload.AlertScreenEventType.BLUETOOTH_NOT_ENABLED +import com.simprints.infra.events.event.domain.models.AuthenticationEvent.AuthenticationPayload +import com.simprints.infra.events.event.domain.models.AuthorizationEvent.AuthorizationPayload.AuthorizationResult.AUTHORIZED +import com.simprints.infra.events.event.domain.models.CandidateReadEvent.CandidateReadPayload.LocalResult +import com.simprints.infra.events.event.domain.models.CandidateReadEvent.CandidateReadPayload.RemoteResult.NOT_FOUND +import com.simprints.infra.events.event.domain.models.ConsentEvent.ConsentPayload.Result.ACCEPTED +import com.simprints.infra.events.event.domain.models.ConsentEvent.ConsentPayload.Type.INDIVIDUAL +import com.simprints.infra.events.event.domain.models.IntentParsingEvent.IntentParsingPayload.IntegrationInfo.COMMCARE +import com.simprints.infra.events.event.domain.models.OneToManyMatchEvent.OneToManyMatchPayload.MatchPool +import com.simprints.infra.events.event.domain.models.OneToManyMatchEvent.OneToManyMatchPayload.MatchPoolType.PROJECT +import com.simprints.infra.events.event.domain.models.RefusalEvent.RefusalPayload.Answer.OTHER +import com.simprints.infra.events.event.domain.models.ScannerConnectionEvent.ScannerConnectionPayload.ScannerGeneration +import com.simprints.infra.events.event.domain.models.ScannerConnectionEvent.ScannerConnectionPayload.ScannerInfo +import com.simprints.infra.events.event.domain.models.callback.CallbackComparisonScore +import com.simprints.infra.events.event.domain.models.callback.ConfirmationCallbackEvent +import com.simprints.infra.events.event.domain.models.callback.EnrolmentCallbackEvent +import com.simprints.infra.events.event.domain.models.callback.ErrorCallbackEvent +import com.simprints.infra.events.event.domain.models.callback.ErrorCallbackEvent.ErrorCallbackPayload.Reason +import com.simprints.infra.events.event.domain.models.callback.IdentificationCallbackEvent +import com.simprints.infra.events.event.domain.models.callback.RefusalCallbackEvent +import com.simprints.infra.events.event.domain.models.callback.VerificationCallbackEvent +import com.simprints.infra.events.event.domain.models.callout.ConfirmationCalloutEvent +import com.simprints.infra.events.event.domain.models.callout.EnrolmentCalloutEvent +import com.simprints.infra.events.event.domain.models.callout.EnrolmentLastBiometricsCalloutEvent +import com.simprints.infra.events.event.domain.models.callout.IdentificationCalloutEvent +import com.simprints.infra.events.event.domain.models.callout.VerificationCalloutEvent +import com.simprints.infra.events.event.domain.models.downsync.EventDownSyncRequestEvent +import com.simprints.infra.events.event.domain.models.downsync.EventDownSyncRequestEvent.QueryParameters +import com.simprints.infra.events.event.domain.models.face.FaceCaptureBiometricsEvent +import com.simprints.infra.events.event.domain.models.face.FaceCaptureConfirmationEvent +import com.simprints.infra.events.event.domain.models.face.FaceCaptureConfirmationEvent.FaceCaptureConfirmationPayload.Result.CONTINUE +import com.simprints.infra.events.event.domain.models.face.FaceCaptureEvent +import com.simprints.infra.events.event.domain.models.face.FaceFallbackCaptureEvent +import com.simprints.infra.events.event.domain.models.face.FaceOnboardingCompleteEvent +import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureBiometricsEvent +import com.simprints.infra.events.event.domain.models.fingerprint.FingerprintCaptureEvent +import com.simprints.infra.events.sampledata.FACE_TEMPLATE_FORMAT +import com.simprints.infra.events.sampledata.SampleDefaults.CREATED_AT +import com.simprints.infra.events.sampledata.SampleDefaults.DEFAULT_METADATA +import com.simprints.infra.events.sampledata.SampleDefaults.DEFAULT_MODULE_ID +import com.simprints.infra.events.sampledata.SampleDefaults.DEFAULT_PROJECT_ID +import com.simprints.infra.events.sampledata.SampleDefaults.DEFAULT_USER_ID +import com.simprints.infra.events.sampledata.SampleDefaults.ENDED_AT +import com.simprints.infra.events.sampledata.SampleDefaults.GUID1 +import com.simprints.infra.events.sampledata.SampleDefaults.GUID2 +import org.junit.Test +import com.simprints.infra.events.event.domain.models.AuthenticationEvent.AuthenticationPayload.UserInfo as AuthenticationUserInfo +import com.simprints.infra.events.event.domain.models.AuthorizationEvent.AuthorizationPayload.UserInfo as AuthorizationUserInfo + +class EventPayloadTest { + + @Test + fun `safe string does not include sensitive data`() { + val sensitiveInfoList = listOf( + DEFAULT_USER_ID.value, + DEFAULT_PROJECT_ID, + "template" + ) + + allEventsList().forEach { + val safeString = it.payload.toSafeString() + sensitiveInfoList.forEach { assertThat(safeString).doesNotContain(it) } + } + } + + private fun allEventsList(): List = listOf( + ConfirmationCallbackEvent(CREATED_AT, true), + EnrolmentCallbackEvent(CREATED_AT, GUID1), + ErrorCallbackEvent(CREATED_AT, Reason.DIFFERENT_PROJECT_ID_SIGNED_IN), + IdentificationCallbackEvent( + createdAt = CREATED_AT, + sessionId = GUID1, + scores = listOf(CallbackComparisonScore(GUID1, 1, TIER_1, AppMatchConfidence.NONE)) + ), + RefusalCallbackEvent(CREATED_AT, "some_reason", "some_extra"), + VerificationCallbackEvent( + createdAt = CREATED_AT, + score = CallbackComparisonScore(GUID1, 1, TIER_1, AppMatchConfidence.NONE) + ), + ConfirmationCalloutEvent(CREATED_AT, DEFAULT_PROJECT_ID, GUID1, GUID2), + EnrolmentCalloutEvent( + createdAt = CREATED_AT, + projectId = DEFAULT_PROJECT_ID, + userId = DEFAULT_USER_ID, + moduleId = DEFAULT_MODULE_ID, + metadata = DEFAULT_METADATA, + ), + EnrolmentLastBiometricsCalloutEvent( + createdAt = CREATED_AT, + projectId = DEFAULT_PROJECT_ID, + userId = DEFAULT_USER_ID, + moduleId = DEFAULT_MODULE_ID, + metadata = DEFAULT_METADATA, + sessionId = GUID1, + ), + IdentificationCalloutEvent( + createdAt = CREATED_AT, + projectId = DEFAULT_PROJECT_ID, + userId = DEFAULT_USER_ID, + moduleId = DEFAULT_MODULE_ID, + metadata = DEFAULT_METADATA, + ), + VerificationCalloutEvent( + createdAt = CREATED_AT, + projectId = DEFAULT_PROJECT_ID, + userId = DEFAULT_USER_ID, + moduleId = DEFAULT_MODULE_ID, + verifyGuid = GUID1, + metadata = DEFAULT_METADATA, + ), + EventDownSyncRequestEvent( + createdAt = CREATED_AT, + endedAt = ENDED_AT, + query = QueryParameters( + moduleId = DEFAULT_MODULE_ID.value, + attendantId = DEFAULT_USER_ID.value, + ), + requestId = "requestId", + ), + FaceCaptureBiometricsEvent( + startTime = CREATED_AT, + face = FaceCaptureBiometricsEvent.FaceCaptureBiometricsPayload.Face( + yaw = 0.0f, + roll = 1.0f, + template = "template", + quality = 1.0f, + format = FACE_TEMPLATE_FORMAT + ), + ), + FaceCaptureConfirmationEvent(CREATED_AT, ENDED_AT, CONTINUE), + FaceCaptureEvent( + startTime = CREATED_AT, + endTime = ENDED_AT, + attemptNb = 0, + qualityThreshold = 1F, + result = FaceCaptureEvent.FaceCapturePayload.Result.VALID, isFallback = true, + face = FaceCaptureEvent.FaceCapturePayload.Face(0F, 1F, 2F, FACE_TEMPLATE_FORMAT), + ), + FaceFallbackCaptureEvent(CREATED_AT, ENDED_AT), + FaceOnboardingCompleteEvent(CREATED_AT, ENDED_AT), + FingerprintCaptureBiometricsEvent( + createdAt = CREATED_AT, + fingerprint = FingerprintCaptureBiometricsEvent.FingerprintCaptureBiometricsPayload.Fingerprint( + finger = IFingerIdentifier.LEFT_3RD_FINGER, + template = "template", + quality = 1, + format = "ISO_19794_2", + ), + id = "someId", + ), + FingerprintCaptureEvent( + createdAt = CREATED_AT, + endTime = ENDED_AT, + finger = IFingerIdentifier.LEFT_THUMB, + qualityThreshold = 10, + result = FingerprintCaptureEvent.FingerprintCapturePayload.Result.BAD_QUALITY, + fingerprint = FingerprintCaptureEvent.FingerprintCapturePayload.Fingerprint( + finger = IFingerIdentifier.LEFT_THUMB, + quality = 8, + format = "ISO_19794_2" + ), + ), + AlertScreenEvent(CREATED_AT, BLUETOOTH_NOT_ENABLED), + AuthenticationEvent( + createdAt = CREATED_AT, + endTime = ENDED_AT, + userInfo = AuthenticationUserInfo(DEFAULT_PROJECT_ID, DEFAULT_USER_ID), + result = AuthenticationPayload.Result.INTEGRITY_SERVICE_ERROR, + ), + AuthorizationEvent( + createdAt = CREATED_AT, + result = AUTHORIZED, + userInfo = AuthorizationUserInfo(DEFAULT_PROJECT_ID, DEFAULT_USER_ID) + ), + CandidateReadEvent(CREATED_AT, ENDED_AT, GUID1, LocalResult.NOT_FOUND, NOT_FOUND), + CompletionCheckEvent(CREATED_AT, true), + ConnectivitySnapshotEvent( + createdAt = CREATED_AT, + connections = listOf( + Connection( + SimNetworkUtils.ConnectionType.MOBILE, + SimNetworkUtils.ConnectionState.CONNECTED + ) + ), + ), + ConsentEvent(CREATED_AT, ENDED_AT, INDIVIDUAL, ACCEPTED), + EnrolmentEventV1(CREATED_AT, GUID2), + EnrolmentEventV2( + createdAt = CREATED_AT, + subjectId = GUID1, + projectId = DEFAULT_PROJECT_ID, + moduleId = DEFAULT_MODULE_ID, + attendantId = DEFAULT_USER_ID, + personCreationEventId = GUID2, + ), + GuidSelectionEvent(CREATED_AT, GUID1), + IntentParsingEvent(CREATED_AT, COMMCARE), + InvalidIntentEvent(CREATED_AT, "REGISTER", mapOf("extra_key" to "value")), + OneToManyMatchEvent( + createdAt = CREATED_AT, + endTime = ENDED_AT, + pool = MatchPool(PROJECT, 100), + matcher = "MATCHER_NAME", + result = listOf(MatchEntry(GUID1, 0F)) + ), + OneToOneMatchEvent( + createdAt = CREATED_AT, + endTime = ENDED_AT, + candidateId = GUID1, + matcher = "MATCHER_NAME", + result = MatchEntry(GUID1, 0F), + fingerComparisonStrategy = FingerComparisonStrategy.SAME_FINGER + ), + PersonCreationEvent( + startTime = CREATED_AT, + fingerprintCaptureIds = listOf(GUID1), + fingerprintReferenceId = GUID1, + faceCaptureIds = listOf(GUID2), + faceReferenceId = GUID2, + ), + RefusalEvent(CREATED_AT, ENDED_AT, OTHER, "other_text"), + ScannerConnectionEvent( + createdAt = CREATED_AT, + scannerInfo = ScannerInfo( + scannerId = "scanner_id", + macAddress = "mac_address", + generation = ScannerGeneration.VERO_1, + hardwareVersion = "hardware_version" + ) + ), + ScannerFirmwareUpdateEvent(CREATED_AT, ENDED_AT, "chip", "v1", "error"), + SuspiciousIntentEvent(CREATED_AT, mapOf("extra_key" to "value")), + ) +} diff --git a/infra/events/src/test/java/com/simprints/infra/events/event/local/EventLocalDataSourceTest.kt b/infra/events/src/test/java/com/simprints/infra/events/event/local/EventLocalDataSourceTest.kt index c46a3e7879..18276fa596 100644 --- a/infra/events/src/test/java/com/simprints/infra/events/event/local/EventLocalDataSourceTest.kt +++ b/infra/events/src/test/java/com/simprints/infra/events/event/local/EventLocalDataSourceTest.kt @@ -352,6 +352,20 @@ internal class EventLocalDataSourceTest { coVerify { scopeDao.count(EventScopeType.SESSION) } } + @Test + fun countClosedEventScopes() = runTest { + eventLocalDataSource.countClosedEventScopes(EventScopeType.SESSION) + + coVerify { scopeDao.countClosed(EventScopeType.SESSION) } + } + + @Test + fun loadAllScopes() = runTest { + eventLocalDataSource.loadAllScopes() + + coVerify { scopeDao.loadAll() } + } + @Test fun saveEventScope() = runTest { mockkStatic("com.simprints.infra.events.event.local.models.DbEventScopeKt") diff --git a/infra/resources/src/main/res/values/styles-text.xml b/infra/resources/src/main/res/values/styles-text.xml index 2966c40cbe..464442290d 100644 --- a/infra/resources/src/main/res/values/styles-text.xml +++ b/infra/resources/src/main/res/values/styles-text.xml @@ -209,6 +209,10 @@ @style/TextAppearance.Simprints.Body1 + +