Refactor analytics and handle opting out

This commit is contained in:
Phillip Thelen 2025-04-08 13:17:09 +02:00
parent 0745a3168c
commit 260a2ffb16
14 changed files with 76 additions and 50 deletions

View file

@ -26,6 +26,7 @@
<meta-data
android:name="firebase_performance_logcat_enabled"
android:value="true" />
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="@string/application_ad_id"/>

View file

@ -3,8 +3,8 @@ package com.habitrpg.android.habitica.extensions
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import com.habitrpg.android.habitica.helpers.CrashReporter
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import java.util.Locale
@ -19,7 +19,7 @@ fun Resources.forceLocale(
updateConfiguration(configuration, displayMetrics)
try {
Firebase.crashlytics.setCustomKey("language", locale.toLanguageTag())
CrashReporter.setCustomKey("language", locale.toLanguageTag())
} catch (_: IllegalStateException) {
// issue with getting firebase
}

View file

@ -242,12 +242,6 @@ class AdHandler(val activity: Activity, val type: AdType, val rewardAction: (Boo
"type" to type.name
)
)
FirebaseAnalytics.getInstance(activity).logEvent(
"adRewardEarned",
bundleOf(
Pair("type", type.name)
)
)
rewardAction(true)
}*/
}

View file

@ -79,7 +79,8 @@ object Analytics {
Amplitude(
Configuration(
context.getString(R.string.amplitude_app_id),
context
context,
optOut = true,
)
)
firebase = FirebaseAnalytics.getInstance(context)
@ -126,4 +127,14 @@ object Analytics {
fun logException(t: Throwable) {
FirebaseCrashlytics.getInstance().recordException(t)
}
fun setAnalyticsConsent(consents: Boolean?) {
if (consents == true) {
firebase.setAnalyticsCollectionEnabled(true)
amplitude.configuration.optOut = false
} else {
firebase.setAnalyticsCollectionEnabled(false)
amplitude.configuration.optOut = true
}
}
}

View file

@ -0,0 +1,14 @@
package com.habitrpg.android.habitica.helpers
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
object CrashReporter {
fun setCustomKey(key: String, value: String) {
Firebase.crashlytics.setCustomKey(key, value)
}
fun recordException(throwable: Throwable) {
Firebase.crashlytics.recordException(throwable)
}
}

View file

@ -23,7 +23,6 @@ import com.android.billingclient.api.acknowledgePurchase
import com.android.billingclient.api.consumePurchase
import com.android.billingclient.api.queryProductDetails
import com.android.billingclient.api.queryPurchasesAsync
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.ApiClient
@ -122,7 +121,7 @@ class PurchaseHandler(
}
else -> {
FirebaseCrashlytics.getInstance().recordException(Throwable(result.debugMessage))
CrashReporter.recordException(Throwable(result.debugMessage))
}
}
}
@ -291,7 +290,7 @@ class PurchaseHandler(
}
if (skuDetailsResult.billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
Log.e("PurchaseHandler", "Failed to load inventory: ${skuDetailsResult.billingResult.debugMessage}")
FirebaseCrashlytics.getInstance().recordException(
CrashReporter.recordException(
Throwable(
"Failed to load inventory: ${skuDetailsResult.billingResult.debugMessage}"
)
@ -496,7 +495,7 @@ class PurchaseHandler(
}
}
processedPurchases.remove(purchase.orderId)
FirebaseCrashlytics.getInstance().recordException(throwable)
CrashReporter.recordException(throwable)
}
suspend fun checkForSubscription(): Purchase? {

View file

@ -5,8 +5,11 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.lifecycle.LifecycleCoroutineScope
import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.helpers.Analytics
import com.habitrpg.android.habitica.helpers.AnalyticsTarget
import com.habitrpg.android.habitica.helpers.EventCategory
import com.habitrpg.android.habitica.helpers.HitType
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.android.habitica.ui.views.SnackbarActivity
@ -196,10 +199,8 @@ class ShowNotificationInteractor(
}
private fun logOnboardingEvents(type: String) {
if (User.ONBOARDING_ACHIEVEMENT_KEYS.contains(type)) {
FirebaseAnalytics.getInstance(activity).logEvent(type, null)
} else if (type == Notification.Type.ACHIEVEMENT_ONBOARDING_COMPLETE.type) {
FirebaseAnalytics.getInstance(activity).logEvent(type, null)
if (User.ONBOARDING_ACHIEVEMENT_KEYS.contains(type) || type == Notification.Type.ACHIEVEMENT_ONBOARDING_COMPLETE.type) {
Analytics.sendEvent(type, EventCategory.BEHAVIOUR, HitType.EVENT, null, AnalyticsTarget.FIREBASE)
}
}
}

View file

@ -45,4 +45,5 @@ open class Preferences : RealmObject(), AvatarPreferences, BaseObject {
var emailNotifications: EmailNotificationsPreference? = null
var autoEquip: Boolean = true
var tasks: UserTaskPreferences? = null
var analyticsConsent: Boolean? = null
}

View file

@ -46,8 +46,6 @@ import androidx.navigation.NavDestination
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import com.google.android.gms.wearable.Wearable
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import com.google.firebase.perf.FirebasePerformance
import com.habitrpg.android.habitica.BuildConfig
import com.habitrpg.android.habitica.MainNavDirections
@ -60,6 +58,7 @@ import com.habitrpg.android.habitica.extensions.hideKeyboard
import com.habitrpg.android.habitica.extensions.updateStatusBarColor
import com.habitrpg.android.habitica.helpers.Analytics
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.CrashReporter
import com.habitrpg.android.habitica.helpers.EventCategory
import com.habitrpg.android.habitica.helpers.HitType
import com.habitrpg.android.habitica.helpers.NotificationOpenHandler
@ -644,9 +643,9 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
}
preferences?.sound?.let { soundManager.soundTheme = it }
val crashlytics = Firebase.crashlytics
crashlytics.setCustomKey("day_start", user.preferences?.dayStart ?: 0)
crashlytics.setCustomKey("timezone_offset", user.preferences?.timezoneOffset ?: 0)
CrashReporter.setCustomKey("day_start", user.preferences?.dayStart ?: 0)
CrashReporter.setCustomKey("timezone_offset", user.preferences?.timezoneOffset ?: 0)
Analytics.setAnalyticsConsent(user.preferences?.analyticsConsent)
displayDeathDialogIfNeeded()
YesterdailyDialog.showDialogIfNeeded(this, user.id, userRepository, taskRepository)
@ -865,7 +864,7 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
errorJob?.cancel()
}
lifecycleScope.launch(Dispatchers.Main) {
if (binding.content.connectionIssueView.visibility == View.VISIBLE) {
if (binding.content.connectionIssueView.isVisible) {
binding.content.connectionIssueView.visibility = View.GONE
}
}

View file

@ -10,7 +10,6 @@ import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import android.widget.Toast
import androidx.core.net.toUri
import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.FragmentAboutBinding
import com.habitrpg.android.habitica.helpers.AppConfigManager
@ -148,7 +147,6 @@ class AboutFragment : BaseMainFragment<FragmentAboutBinding>() {
private fun doTheThing() {
val context = context ?: return
FirebaseAnalytics.getInstance(context).logEvent("found_easter_egg", null)
DataBindingUtils.loadImage(context, "Pet-Sabretooth-Base") { bitmap ->
mainActivity?.runOnUiThread {
mainActivity?.let {

View file

@ -19,6 +19,10 @@ import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.extensions.setNavigationBarDarkIcons
import com.habitrpg.android.habitica.helpers.Analytics
import com.habitrpg.android.habitica.helpers.AnalyticsTarget
import com.habitrpg.android.habitica.helpers.EventCategory
import com.habitrpg.android.habitica.helpers.HitType
import com.habitrpg.android.habitica.helpers.SoundManager
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.helpers.ToolbarColorHelper
@ -72,12 +76,13 @@ abstract class BaseMainFragment<VB : ViewBinding> : BaseFragment<VB>() {
showToolbar()
enableToolbarScrolling()
}
context?.let {
FirebaseAnalytics.getInstance(it).logEvent(
"fragment_view",
bundleOf(Pair("fragment", this::class.java.canonicalName))
)
}
Analytics.sendEvent(
"fragment_view",
EventCategory.NAVIGATION,
HitType.PAGEVIEW,
mapOf("fragment" to (this::class.java.canonicalName ?: "")),
AnalyticsTarget.FIREBASE
)
return super.onCreateView(inflater, container, savedInstanceState)
}

View file

@ -15,7 +15,6 @@ import androidx.fragment.app.viewModels
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator
import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.FragmentViewpagerBinding
import com.habitrpg.android.habitica.models.social.Group
@ -71,12 +70,6 @@ class GuildFragment : BaseMainFragment<FragmentViewpagerBinding>() {
setViewPagerAdapter()
setFragments()
if (viewModel.groupID == "f2db2a7f-13c5-454d-b3ee-ea1f5089e601") {
context?.let {
FirebaseAnalytics.getInstance(it).logEvent("opened_no_party_guild", null)
}
}
viewModel.retrieveGroup { }
}

View file

@ -7,10 +7,13 @@ import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.android.habitica.helpers.Analytics
import com.habitrpg.android.habitica.helpers.AnalyticsTarget
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.EventCategory
import com.habitrpg.android.habitica.helpers.HitType
import com.habitrpg.android.habitica.helpers.PurchaseHandler
import com.habitrpg.android.habitica.helpers.PurchaseTypes
import com.habitrpg.android.habitica.interactors.InsufficientGemsUseCase
@ -100,9 +103,12 @@ class InsufficientGemsDialog(val parentActivity: Activity, var gemPrice: Int) :
purchaseButton.isVisible = true
purchaseButton?.setOnClickListener {
FirebaseAnalytics.getInstance(context).logEvent(
Analytics.sendEvent(
"purchased_gems_from_insufficient",
bundleOf(Pair("gemPrice", gemPrice), Pair("sku", ""))
EventCategory.BEHAVIOUR,
HitType.EVENT,
mapOf(Pair("gemPrice", gemPrice), Pair("sku", "")),
AnalyticsTarget.FIREBASE
)
MainScope().launchCatching {
insufficientGemsUseCase.callInteractor(

View file

@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.ui.views.shops
import android.app.Activity
import android.content.Context
import android.graphics.drawable.BitmapDrawable
import android.media.metrics.Event
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
@ -12,7 +13,6 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toDrawable
import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope
import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.InventoryRepository
@ -23,6 +23,7 @@ import com.habitrpg.android.habitica.extensions.addCancelButton
import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.android.habitica.extensions.getShortRemainingString
import com.habitrpg.android.habitica.helpers.Analytics
import com.habitrpg.android.habitica.helpers.AnalyticsTarget
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.EventCategory
import com.habitrpg.android.habitica.helpers.HapticFeedbackManager
@ -437,13 +438,16 @@ class PurchaseDialog(
}
private fun buyItem(quantity: Int) {
FirebaseAnalytics.getInstance(context).logEvent(
Analytics.sendEvent(
"item_purchased",
bundleOf(
Pair("shop", shopIdentifier),
Pair("type", shopItem.purchaseType),
Pair("key", shopItem.key)
)
EventCategory.BEHAVIOUR,
HitType.EVENT,
mapOf(
"shop" to (shopIdentifier ?: ""),
"type" to shopItem.purchaseType,
"key" to shopItem.key
),
AnalyticsTarget.FIREBASE
)
HapticFeedbackManager.tap(buyButton)
val snackbarText = arrayOf("")