From 5b022398963576de0f51c81ec7f25ae2d220b331 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Jan 2025 16:19:43 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/tap/features/main/MainViewModel.kt | 30 ++++++- common/build.gradle.kts | 7 ++ .../common/keyboard/KeyboardValidator.kt | 83 +++++++++++++++++++ .../models/event/TechAnalyticsEvent.kt | 12 +++ 4 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 common/src/main/kotlin/com/tangem/common/keyboard/KeyboardValidator.kt diff --git a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt index 29dc39ea0b..83fb5f942c 100644 --- a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt @@ -3,7 +3,10 @@ package com.tangem.tap.features.main import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tangem.blockchainsdk.BlockchainSDKFactory +import com.tangem.common.keyboard.KeyboardValidator import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.event.TechAnalyticsEvent import com.tangem.core.decompose.di.GlobalUiMessageSender import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.R @@ -44,8 +47,9 @@ internal class MainViewModel @Inject constructor( private val fetchStakingTokensUseCase: FetchStakingTokensUseCase, private val apiConfigsManager: ApiConfigsManager, private val fetchUserCountryUseCase: FetchUserCountryUseCase, - @GlobalUiMessageSender - private val messageSender: UiMessageSender, + @GlobalUiMessageSender private val messageSender: UiMessageSender, + private val keyboardValidator: KeyboardValidator, + private val analyticsEventHandler: AnalyticsEventHandler, getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, ) : ViewModel() { @@ -74,6 +78,8 @@ internal class MainViewModel @Inject constructor( fetchStakingTokens() deleteDeprecatedLogsUseCase() + + sendKeyboardIdentifierEvent() } /** Loading the resources needed to run the application */ @@ -252,4 +258,24 @@ internal class MainViewModel @Inject constructor( private fun onBottomSheetDismissed() { listenToFlipsUseCase.changeUpdateEnabled(isUpdateEnabled = true) } + + private fun sendKeyboardIdentifierEvent() { + viewModelScope.launch { + val keyboardId = keyboardValidator.getKeyboardId() + + if (keyboardId != null) { + Timber.d("Keyboard ID: https://play.google.com/store/apps/details?id=${keyboardId.getPackageName()}") + + analyticsEventHandler.send( + event = TechAnalyticsEvent.KeyboardIdentifier( + id = keyboardId.value, + packageName = keyboardId.getPackageName(), + isTrusted = keyboardValidator.validate(id = keyboardId), + ), + ) + } else { + Timber.e("Unable to get keyboard identifier") + } + } + } } \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts index a1afc8ea37..055efc6a2a 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,6 +1,8 @@ plugins { alias(deps.plugins.android.library) alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.hilt.android) id("configuration") } @@ -25,4 +27,9 @@ dependencies { implementation(deps.test.junit) implementation(deps.test.truth) + + // region DI + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) + // end } \ No newline at end of file diff --git a/common/src/main/kotlin/com/tangem/common/keyboard/KeyboardValidator.kt b/common/src/main/kotlin/com/tangem/common/keyboard/KeyboardValidator.kt new file mode 100644 index 0000000000..76850cee8a --- /dev/null +++ b/common/src/main/kotlin/com/tangem/common/keyboard/KeyboardValidator.kt @@ -0,0 +1,83 @@ +package com.tangem.common.keyboard + +import android.content.Context +import android.provider.Settings +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Keyboard validator + * + * @property context application context + */ +@Singleton +class KeyboardValidator @Inject constructor( + @ApplicationContext private val context: Context, +) { + + /** Get keyboard identifier [KeyboardID] */ + fun getKeyboardId(): KeyboardID? { + val id = Settings.Secure.getString( + this.context.contentResolver, + Settings.Secure.DEFAULT_INPUT_METHOD, + ) ?: return null + + return KeyboardID(value = id) + } + + /** Check if [id] is trusted */ + fun validate(id: KeyboardID): Boolean = trustedIdentifiers.contains(id.getPackageName()) + + /** + * Keyboard identifier + * + * @property value value + */ + @JvmInline + value class KeyboardID(val value: String) { + + /** Get package name */ + fun getPackageName(): String? = value.split("/").firstOrNull() + } + + private companion object { + + val trustedIdentifiers = listOf( + // Google + "com.android.inputmethod.latin", + "com.google.android.inputmethod.latin", + "com.google.android.tts", + + // Samsung + "com.sec.android.inputmethod.latin", + "com.sec.android.inputmethod/.SamsungVoiceIME", + "com.samsung.android.honeyboard", + + // Microsoft + "com.microsoft.SwiftKeyApp", + "com.touchtype.swiftkey", + "com.touchtype.swiftkey.beta", + + // HtC + "com.htc.sense.ime.langpack.tger", + + // LG + "com.lge.ime", + + // Huawei + "com.huawei.ohos.inputmethod", + + // Third party + "com.menny.android.anysoftkeyboard", + "ch.icoaching.wrio", + "rkr.simplekeyboard.inputmethod", + "keepass2android.keepass2android", + "keepass2android.keepass2android_nonet", + "com.softwarevalencia.openboard.inputmethod.latin", + "kl.ime.oh", + "com.syntellia.fleksy.keyboard", + "org.pocketworkstation.pckeyboard", + ) + } +} \ No newline at end of file diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/TechAnalyticsEvent.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/TechAnalyticsEvent.kt index 7d7460a633..3e71bc838b 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/TechAnalyticsEvent.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/TechAnalyticsEvent.kt @@ -23,4 +23,16 @@ sealed class TechAnalyticsEvent( FULLY, } } + + class KeyboardIdentifier(id: String, packageName: String?, isTrusted: Boolean) : TechAnalyticsEvent( + event = "Keyboard Identifier", + params = buildMap { + put("Id", id) + packageName?.let { + put("Package", it) + put("GPUrl", "https://play.google.com/store/apps/details?id=$packageName") + } + put("isTrusted", isTrusted.toString()) + }, + ) } \ No newline at end of file