Updated on 2026-08-14

This commit is contained in:
Tangem 2020-04-03 06:04:39 +00:00
commit 640f99a507
21 changed files with 158 additions and 13 deletions

View file

@ -2,6 +2,8 @@ package com.tangem
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.tangem.data.dp.PrefsManager
@ -14,6 +16,7 @@ import com.tangem.server_android.data.LocalStorage
import com.tangem.tangem_card.data.Issuer
import com.tangem.tangem_sdk.android.data.Firmwares
import com.tangem.tangem_sdk.android.data.PINStorage
import com.tangem.util.Analytics
import com.tangem.wallet.BuildConfig
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
@ -76,6 +79,9 @@ class App : Application() {
}
)
}
FirebaseAnalytics.getInstance(this).setAnalyticsCollectionEnabled(Analytics.isEnabled())
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(Analytics.isEnabled())
}
private fun buildToastHelperComponent(): ToastHelperComponent {

View file

@ -3,6 +3,7 @@ package com.tangem.data.dp
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import com.orhanobut.hawk.Hawk
import com.tangem.Constant
import com.tangem.tangem_card.reader.CardCrypto
@ -10,6 +11,7 @@ import com.tangem.tangem_card.reader.CardCrypto
class PrefsManager {
companion object {
const val PREF_NAME = "tangem_access"
@SuppressLint("StaticFieldLeak")
private var instance: PrefsManager? = null
@ -41,6 +43,11 @@ class PrefsManager {
Hawk.delete(Constant.PREF_LAST_WALLET_ADDRESS)
}
fun getSettingsBoolean(key: Int, default: Boolean): Boolean {
return PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(key), default)
}
fun appendCid(newCid: String) {
val prevCids: String = Hawk.get("CID_Key", "")
if (!prevCids.contains(newCid)) Hawk.put("CID_Key", "$prevCids$newCid, ")

View file

@ -20,6 +20,8 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf
import androidx.lifecycle.ViewModelProviders
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.data.Blockchain
@ -46,9 +48,7 @@ import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.ui.fragment.pin.PinRequestFragment
import com.tangem.ui.navigation.NavigationResultListener
import com.tangem.util.CommonUtil
import com.tangem.util.LOG
import com.tangem.util.UtilHelper
import com.tangem.util.*
import com.tangem.util.extensions.colorFrom
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.CoinEngineFactory
@ -117,6 +117,9 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
setCardStoreLink()
FirebaseAnalytics.getInstance(requireActivity())
.logEvent(AnalyticsEvent.READY_TO_SCAN.event, bundleOf())
}
private fun setCardStoreLink() {
@ -270,6 +273,9 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
val ctx = TangemContext(card)
FirebaseAnalytics.getInstance(requireActivity())
.logEvent(AnalyticsEvent.CARD_IS_SCANNED.event, Analytics.setCardData(ctx))
when {
card.status == TangemCard.Status.Loaded -> lastTag?.let {
val engineCoin = CoinEngineFactory.create(ctx)
@ -315,6 +321,7 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
}
} else {
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
// remove last UIDs because of error and no card read
rlProgressBar.post {
context?.let { Toast.makeText(it, R.string.general_notification_scan_again, Toast.LENGTH_SHORT).show() }

View file

@ -12,6 +12,7 @@ import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.core.os.bundleOf
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.tangem_card.reader.CardProtocol
@ -151,6 +152,7 @@ class PurgeFragment : BaseFragment(), NfcAdapter.ReaderCallback, CardProtocol.No
navigateToDestination(R.id.action_purgeFragment_to_mainFragment)
}
} else {
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN) {
progressBar?.post {
progressBar?.progress = 100

View file

@ -6,11 +6,15 @@ import android.nfc.Tag
import android.os.Bundle
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.core.os.bundleOf
import com.google.firebase.analytics.FirebaseAnalytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.tangem_card.util.Util
import com.tangem.ui.activity.MainActivity
import com.tangem.ui.event.TransactionFinishWithSuccess
import com.tangem.util.AnalyticsEvent
import com.tangem.util.AnalyticsParam
import com.tangem.util.UtilHelper
import com.tangem.wallet.CoinEngine
import com.tangem.wallet.CoinEngineFactory
@ -40,7 +44,7 @@ class SendTransactionFragment : BaseFragment(), NfcAdapter.ReaderCallback {
if (success) {
App.pendingTransactionsStorage.putTransaction(ctx.card, Util.bytesToHex(tx), engine.pendingTransactionTimeoutInSeconds())
finishWithSuccess()
}else
} else
finishWithError(ctx.error)
}
@ -72,6 +76,11 @@ class SendTransactionFragment : BaseFragment(), NfcAdapter.ReaderCallback {
}
private fun finishWithSuccess() {
activity?.let {
val params = bundleOf(AnalyticsParam.BLOCKCHAIN.param to ctx.blockchainName)
FirebaseAnalytics.getInstance(it).logEvent(AnalyticsEvent.TRANSACTION_IS_SENT.event, params)
}
val transactionFinishWithSuccess = TransactionFinishWithSuccess()
EventBus.getDefault().post(transactionFinishWithSuccess)

View file

@ -1,18 +1,24 @@
package com.tangem.ui.fragment
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf
import androidx.navigation.fragment.NavHostFragment
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.util.Analytics
import com.tangem.util.AnalyticsEvent
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_settings.*
class SettingsFragment : PreferenceFragmentCompat() {
class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_main)
@ -40,6 +46,32 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == getString(R.string.pref_analytics)) {
activity?.let {
val analytics = FirebaseAnalytics.getInstance(it)
if (!Analytics.isEnabled()) {
analytics.logEvent(AnalyticsEvent.ANALYTICS_TURNED_OFF.event, bundleOf())
}
analytics.setAnalyticsCollectionEnabled(Analytics.isEnabled())
}
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(Analytics.isEnabled())
}
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences
.registerOnSharedPreferenceChangeListener(this);
}
override fun onPause() {
super.onPause()
preferenceScreen.sharedPreferences
.unregisterOnSharedPreferenceChangeListener(this);
}
override fun onDestroyView() {
(activity as AppCompatActivity).setSupportActionBar(toolbar)
super.onDestroyView()

View file

@ -12,6 +12,7 @@ import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.data.Blockchain
@ -26,7 +27,6 @@ import com.tangem.ui.activity.MainActivity
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.ui.fragment.BaseFragment
import com.tangem.ui.fragment.wallet.LoadedWalletViewModel
import com.tangem.util.LOG
import com.tangem.util.UtilHelper
import com.tangem.wallet.*
@ -232,6 +232,7 @@ class IdFragment : BaseFragment(), NfcAdapter.ReaderCallback,
}
} else {
// remove last UIDs because of error and no card read
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
rlProgressBar?.post {
lastReadSuccess = false
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported)

View file

@ -11,12 +11,12 @@ import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.tangem_card.data.TangemCard
import com.tangem.tangem_card.reader.CardProtocol
import com.tangem.tangem_card.tasks.OneTouchSignTask
import com.tangem.tangem_card.tasks.SignTask
import com.tangem.tangem_card.util.Util
import com.tangem.tangem_sdk.android.data.PINStorage
import com.tangem.tangem_sdk.android.nfc.NfcDeviceAntennaLocation
@ -24,7 +24,6 @@ import com.tangem.tangem_sdk.android.reader.NfcReader
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD_UID
import com.tangem.tangem_sdk.data.asBundle
import com.tangem.ui.SignTransactionFragment
import com.tangem.ui.activity.MainActivity
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
@ -223,6 +222,7 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener,
mpFinishSignSound.start()
} else {
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
lastReadSuccess = false
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
progressBar?.post {

View file

@ -11,6 +11,7 @@ import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.tangem_card.reader.CardProtocol
@ -190,6 +191,7 @@ class WriteIdFragment : BaseFragment(), NavigationResultListener,
} else {
lastReadSuccess = false
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
progressBar?.post {
progressBar?.progress = 100

View file

@ -10,6 +10,7 @@ import android.os.Bundle
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.tangem_card.data.TangemCard
@ -112,6 +113,7 @@ class PinSwapFragment : BaseFragment(), NfcAdapter.ReaderCallback, CardProtocol.
swapPinTask = null
if (cardProtocol != null) {
cardProtocol.error?.let { FirebaseCrashlytics.getInstance().recordException(it) }
when {
cardProtocol.error == null -> progressBar!!.post {
progressBar!!.progress = 100

View file

@ -9,6 +9,7 @@ import android.nfc.tech.IsoDep
import android.os.Bundle
import android.view.View
import android.widget.Toast
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.tangem_card.reader.CardProtocol
@ -101,6 +102,7 @@ class CreateNewWalletFragment : BaseFragment(), NfcAdapter.ReaderCallback, CardP
return@post
}
} else {
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
lastReadSuccess = false
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN) {
progressBar?.post {

View file

@ -13,6 +13,7 @@ import android.os.Bundle
import android.text.Html
import android.view.View
import android.widget.Toast
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.tangem_card.data.TangemCard
@ -181,6 +182,7 @@ class EmptyWalletFragment : BaseFragment(), NavigationResultListener,
this.cardProtocol = cardProtocol
}
} else {
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
// remove last UIDs because of error and no card read
progressBar?.post {
lastReadSuccess = false

View file

@ -337,6 +337,7 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte
@Subscribe
fun onTransactionFinishWithSuccess(transactionFinishWithSuccess: TransactionFinishWithSuccess) {
refreshAction = Runnable { refresh() }
ctx.card.remainingSignatures -= 1
srl?.postDelayed(refreshAction, 10000)
}
@ -478,6 +479,7 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte
Constant.REQUEST_CODE_SEND_TRANSACTION, Constant.REQUEST_CODE_RECEIVE_TRANSACTION -> {
if (resultCode == Activity.RESULT_OK) {
// ctx.card.remainingSignatures = ctx.card.remainingSignatures - 1
ctx.coinData?.clearInfo()
srl?.postDelayed({ this.refresh() }, 5000)
srl?.isRefreshing = true

View file

@ -0,0 +1,39 @@
package com.tangem.util
import android.os.Bundle
import androidx.core.os.bundleOf
import com.tangem.data.dp.PrefsManager
import com.tangem.wallet.R
import com.tangem.wallet.TangemContext
enum class AnalyticsEvent(val event: String) {
CARD_IS_SCANNED("card_is_scanned"),
TRANSACTION_IS_SENT("transaction_is_sent"),
READY_TO_SCAN("ready_to_scan"),
READY_TO_SIGN("ready_to_sign"),
SIGNED("signed"),
ANALYTICS_TURNED_OFF("analytics_turned_off"),
;
}
enum class AnalyticsParam(val param: String) {
BLOCKCHAIN("blockchain"),
BATCH_ID("batch_id"),
FIRMWARE("firmware"),
;
}
class Analytics {
companion object {
fun setCardData(ctx: TangemContext): Bundle {
return bundleOf(
AnalyticsParam.BLOCKCHAIN.param to ctx.blockchainName,
AnalyticsParam.BATCH_ID.param to ctx.card.batch,
AnalyticsParam.FIRMWARE.param to ctx.card.firmwareVersion
)
}
fun isEnabled(): Boolean {
return PrefsManager.getInstance().getSettingsBoolean(R.string.pref_analytics, true)
}
}
}

View file

@ -4,10 +4,12 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.google.firebase.crashlytics.FirebaseCrashlytics;
import com.tangem.Constant;
import com.tangem.data.Blockchain;
import com.tangem.tangem_card.data.TangemCard;
import com.tangem.tangem_sdk.data.TangemCardExtensionsKt;
import com.tangem.util.AnalyticsParam;
public class TangemContext {
// public static final String EXTRA_BLOCKCHAIN_DATA = "BLOCKCHAIN_DATA";
@ -58,7 +60,7 @@ public class TangemContext {
public String getBlockchainName() {
Blockchain blockchain = getBlockchain();
if (blockchain == Blockchain.Token || blockchain == Blockchain.RootstockToken) {
return card.getTokenSymbol()+ "<br><small><small> " + getBlockchain().getOfficialName() + " smart contract token</small></small>";
return card.getTokenSymbol() + "<br><small><small> " + getBlockchain().getOfficialName() + " smart contract token</small></small>";
}
if (blockchain == Blockchain.NftToken) {
return card.getTokenSymbol().substring(4) + "<br><small><small> " + getBlockchain().getOfficialName() + " non-fungible token</small></small>";
@ -67,7 +69,7 @@ public class TangemContext {
return card.getTokenSymbol() + "<br><small><small> " + getBlockchain().getOfficialName() + " asset</small></small>";
}
if (blockchain == Blockchain.StellarTag) {
return "TANGEM TAG<br><small><small> "+ getBlockchain().getOfficialName() + " non-fungible token </small></small>";
return "TANGEM TAG<br><small><small> " + getBlockchain().getOfficialName() + " non-fungible token </small></small>";
}
return blockchain.getOfficialName();
}
@ -97,6 +99,11 @@ public class TangemContext {
}
public void setError(String error) {
if (error != null) {
FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
crashlytics.setCustomKey(AnalyticsParam.BLOCKCHAIN.getParam(), getBlockchainName());
crashlytics.recordException(new Exception(error));
}
this.error = error;
}

View file

@ -28,6 +28,7 @@
<string name="settings_title">Settings</string>
<string name="settings_category_сommon">Common</string>
<string name="settings_option_manual_editing_fee">Manual editing Fee</string>
<string name="settings_option_analytics">Share anonymous data with developers</string>
<string name="settings_option_encryption_modes">Encryption modes</string>
<string-array name="settings_encryption_modes_entries">
<item>None</item>

View file

@ -26,6 +26,7 @@
<string name="pref_category_common" translatable="false">category_common</string>
<string name="pref_manual_editing_fee" translatable="false">manual_editing_fee</string>
<string name="pref_analytics" translatable="false">analytics</string>
<string name="pref_select_nodes" translatable="false">select_nodes</string>
<string name="pref_encryption_modes" translatable="false">encryption_modes</string>

View file

@ -4,11 +4,16 @@
android:key="@string/pref_category_common"
android:title="@string/settings_category_сommon">
<CheckBoxPreference
<SwitchPreference
android:defaultValue="false"
android:key="@string/pref_manual_editing_fee"
android:title="@string/settings_option_manual_editing_fee" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/pref_analytics"
android:title="@string/settings_option_analytics" />
<Preference
android:key="@string/pref_select_nodes"
android:title="@string/settings_option_select_nodes" />

View file

@ -11,6 +11,8 @@ import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
import com.tangem.Constant
import com.tangem.tangem_card.reader.CardProtocol
@ -26,6 +28,8 @@ import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.ui.fragment.BaseFragment
import com.tangem.ui.navigation.NavigationResultListener
import com.tangem.util.Analytics
import com.tangem.util.AnalyticsEvent
import com.tangem.util.LOG
import com.tangem.wallet.CoinEngine
import com.tangem.wallet.CoinEngineFactory
@ -34,7 +38,6 @@ import com.tangem.wallet.TangemContext
import kotlinx.android.synthetic.main.layout_progress_horizontal.*
import kotlinx.android.synthetic.main.layout_touch_card.*
import kotlinx.android.synthetic.tangemAccess.fragment_sign_transaction.*
import java.lang.IllegalArgumentException
class SignTransactionFragment : BaseFragment(), NavigationResultListener,
@ -88,6 +91,9 @@ class SignTransactionFragment : BaseFragment(), NavigationResultListener,
tvCardID.text = ctx.card!!.cidDescription
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar.visibility = View.INVISIBLE
FirebaseAnalytics.getInstance(requireActivity())
.logEvent(AnalyticsEvent.READY_TO_SIGN.event, Analytics.setCardData(ctx))
}
override fun onPause() {
@ -175,6 +181,10 @@ class SignTransactionFragment : BaseFragment(), NavigationResultListener,
signTransactionTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
FirebaseAnalytics.getInstance(requireActivity())
.logEvent(AnalyticsEvent.SIGNED.event, Analytics.setCardData(ctx))
rlProgressBar?.post { rlProgressBar?.visibility = View.GONE }
progressBar?.post {
@ -185,6 +195,7 @@ class SignTransactionFragment : BaseFragment(), NavigationResultListener,
mpFinishSignSound.start()
} else {
lastReadSuccess = false
FirebaseCrashlytics.getInstance().recordException(cardProtocol.error)
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
progressBar?.post {
progressBar?.progress = 100

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Before After
Before After

View file

@ -1,5 +1,8 @@
package com.tangem.common
import com.tangem.commands.EllipticCurve
import com.tangem.crypto.CryptoUtils.generatePublicKey
/**
* Contains data relating to a Tangem card. It is used in constructing all the commands,
@ -27,4 +30,8 @@ enum class EncryptionMode(val code: Byte) {
STRONG(0x2)
}
class KeyPair(val publicKey: ByteArray, val privateKey: ByteArray)
class KeyPair(val publicKey: ByteArray, val privateKey: ByteArray) {
constructor(privateKey: ByteArray, curve: EllipticCurve = EllipticCurve.Secp256k1) :
this(generatePublicKey(privateKey, curve), privateKey)
}