Skip to content

Feat: New analytics using GA4 [DEV]#20582

Open
criticalAY wants to merge 3 commits intoankidroid:mainfrom
criticalAY:feat/analytics-ga4
Open

Feat: New analytics using GA4 [DEV]#20582
criticalAY wants to merge 3 commits intoankidroid:mainfrom
criticalAY:feat/analytics-ga4

Conversation

@criticalAY
Copy link
Copy Markdown
Contributor

@criticalAY criticalAY commented Mar 25, 2026

Community & Privacy Context

Before diving into the technical details, we want to be completely transparent about what this PR does and does not do. We have had analytics in the app for years to help us understand feature usage, but the system recently broke. This PR is strictly a bugfix to restore that existing functionality.

Because community trust is our top priority, please note:

  • It is strictly opt-in: Absolutely no data is collected unless a user explicitly consents.
  • It is privacy-preserving: We do not collect personal data. We only use simple counters to track feature usage, which tells us what the community needs us to focus on.
  • It is transparent: We are open to sharing our aggregated analytics with the world in the future if the platform allows for it.
  • Team Aligned: This update has been thoroughly discussed and has the full backing of the core maintainers.

Technical Implementation

I am updating the analytics usage in project, I forked library that Mike had implemented and after going through it, I create my own library with Kt and implemented whatever was there in java one(help needed here as the library needs more test which I am adding but any help is welcome).

Steps implemented:

Fixes

Approach

NA, the commits are faily self explaining

How Has This Been Tested?

Pixel 10

Learning (optional, can help others)

A lot of it tbh, the migration in Kotlin took me almost 2 days, then how to publish the library on maven (I had published the library on jitpack before but never on maven so this was new, Mike can give me valuable input since he is maintaining a lot fo things)

Checklist

Please, go through these checks before submitting the PR.

  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code
  • UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)
  • UI Changes: You have tested your change using the Google Accessibility Scanner

@criticalAY
Copy link
Copy Markdown
Contributor Author

criticalAY commented Mar 25, 2026

TODO for me: Update the analytics usage in the whole app i.e. find the places where it is not there as of now and fix it

Copy link
Copy Markdown
Member

@david-allison david-allison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is AWESOME

Before we look into this further, we need commits to:

  • Remove all past consent for analytics, and use another preference key
  • Modify our consent dialog to be opt-in, not opt-out (flip a checkbox default state)

EDIT: Split the refactorings out into 'move' commits whenever you can. so we don't lose git history

@criticalAY criticalAY force-pushed the feat/analytics-ga4 branch 5 times, most recently from 6764e26 to 360b343 Compare March 25, 2026 17:18
Copy link
Copy Markdown
Member

@BrayanDSO BrayanDSO left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, #20584 and #20585 should be part of this PR. The third commit duplicates a lot of stuff which only makes sense when the old code is removed.

Copy link
Copy Markdown
Member

@BrayanDSO BrayanDSO left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meant to request changes

@BrayanDSO BrayanDSO added Needs Author Reply Waiting for a reply from the original author and removed Needs Review labels Mar 29, 2026
@criticalAY criticalAY force-pushed the feat/analytics-ga4 branch from 360b343 to bb640a0 Compare April 4, 2026 15:21
@criticalAY
Copy link
Copy Markdown
Contributor Author

IMO, #20584 and #20585 should be part of this PR. The third commit duplicates a lot of stuff which only makes sense when the old code is removed.

The commit was originally part of this PR but it was getting bit messy so wanted to make review easier, I think it's fine(want to save time)

@criticalAY criticalAY added Needs Review and removed Needs Author Reply Waiting for a reply from the original author labels Apr 4, 2026
@criticalAY criticalAY force-pushed the feat/analytics-ga4 branch from bb640a0 to d97afae Compare April 8, 2026 11:51
@criticalAY criticalAY changed the title Feat: New analytics using GA4 Feat: New analytics using GA4 [DEV] Apr 9, 2026
@criticalAY
Copy link
Copy Markdown
Contributor Author

@david-allison what's blocking here now?

Comment thread AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidUsageAnalytics.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidUsageAnalytics.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidUsageAnalytics.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidUsageAnalytics.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidUsageAnalytics.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidUsageAnalytics.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidUsageAnalytics.kt Outdated
Copy link
Copy Markdown
Member

@BrayanDSO BrayanDSO left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work. Getting analytics back is going to be amazing. Thanks!

@BrayanDSO BrayanDSO added the Needs Second Approval Has one approval, one more approval to merge label Apr 28, 2026
Copy link
Copy Markdown
Member

@david-allison david-allison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to nitpick. From what I see in the diff, this no longer matches the description

  • The issue should not be closed
  • This functionality is not enabled
  • The old implementation is not removed

* analytics client id. Keeping it isolated avoids exposing the id through
* preference screens, backups, or bulk prefs operations on the main file.
*/
private const val ANALYTICS_PREFS = "analyticsPrefs"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would this be impacted by profiles?

@Volatile private var optIn = false

private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val clientId: String by lazy { getOrCreateClientId(AnkiDroidApp.instance) }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that we have rare Android-level bugs where AnkiDroidApp.instance is not defined.

This needs to be handled: analytics is a startup concern which must not crash

apiSecret = BuildConfig.ANALYTICS_API_KEY
appName = appContext.getString(R.string.app_name)
appVersion = BuildConfig.VERSION_NAME
enabled = true
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this not use optIn?


serviceScope.launch {
runCatching { analytics?.flush() }.onFailure { e ->
Timber.e(e, "Failed to flush analytics")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Timber.e(e, "Failed to flush analytics")
Timber.w(e, "Failed to flush analytics")

Comment on lines +169 to +175
private fun getRootCause(t: Throwable): Throwable {
var result = t
while (result.cause != null && result.cause !== result) {
result = result.cause!!
}
return result
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would make this an extension method and move it to common

Throwable.getRootCause

Comment on lines +201 to +207
var isEnabled: Boolean
get() = optIn
set(value) {
AnkiDroidApp.instance.sharedPrefs().edit {
putBoolean(ANALYTICS_OPTIN_KEY, value)
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't set optIn

val analytics = analytics ?: return
analytics
.exception(clientId)
.description(description.take(150))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain why 150

}
}

fun sendAnalyticsScreenView(screen: Any) = sendAnalyticsScreenView(screen.javaClass.simpleName)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

document usage and what this outputs: com.ichi2.anki.DeckPicker or DeckPicker

Comment thread gradle/libs.versions.toml
desugar-jdk-libs-nio = "2.1.5"
drawer = "1.0.3"
espresso = '3.7.0'
googleAnalyticsKt = "1.2.1"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment linking the GitHub repo

return samplePercentage
}

private fun initializePrefKeys(context: Context) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quickly document what this does

@criticalAY criticalAY force-pushed the feat/analytics-ga4 branch from 32db738 to 59e60b1 Compare May 2, 2026 05:57
@criticalAY criticalAY force-pushed the feat/analytics-ga4 branch from 59e60b1 to 76fb29e Compare May 2, 2026 06:01
@criticalAY criticalAY requested a review from david-allison May 2, 2026 06:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Analytics Needs Second Approval Has one approval, one more approval to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update to net.mikehardy:google-analytics-java:2.0.11 (needs minSdkVersion >= 24)

3 participants