Skip to content

FOSS Release Setup#11

Merged
SurjitSahoo merged 8 commits intomainfrom
feature/compliance-cleanup
Feb 14, 2026
Merged

FOSS Release Setup#11
SurjitSahoo merged 8 commits intomainfrom
feature/compliance-cleanup

Conversation

@SurjitSahoo
Copy link
Owner

@SurjitSahoo SurjitSahoo commented Feb 14, 2026

Summary by CodeRabbit

  • New Features

    • App now ships as two distributions: FOSS (no analytics/crash reporting) and Play (full analytics & crash reporting).
    • Library shows an initialization/loading state for clearer startup feedback.
  • User-facing Changes

    • Crash reporting toggle and analytics consent prompt shown only in Play distribution.
    • Input fields use a privacy masking modifier.
  • Documentation

    • Branding renamed to "Kahani".
    • Expanded app description and refreshed short description.
  • Chores

    • Build/config reorganized to support distribution flavors; analytics library version updated.

@coderabbitai
Copy link

coderabbitai bot commented Feb 14, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Refactors analytics and crash reporting into abstract interfaces, adds FOSS/Play product flavors with conditional dependencies and build changes, replaces Clarity/Firebase implementations with flavor-specific bindings (no-op for FOSS, Clarity+Firebase for Play), and updates CI/release workflows and UI masking usage.

Changes

Cohort / File(s) Summary
CI / Release
.github/workflows/build_app.yml, .github/workflows/release.yml
Changed Gradle tasks to assembleFossRelease/assemblePlayRelease, removed changelog step, switched release artifact to play-release APK, and enabled GitHub-generated release notes.
Top-level Gradle
build.gradle.kts, gradle/libs.versions.toml
Removed global Google Services / Crashlytics plugin aliases; bumped Clarity version to 3.8.1.
App Gradle / Flavors
app/build.gradle.kts
Added flavorDimensions and foss/play productFlavors, conditional plugin application for non-FOSS builds, flavor-specific disabling of GoogleServices/Crashlytics tasks, and revised signingConfig resolution.
Core abstractions
app/src/main/kotlin/org/grakovne/lissen/analytics/AnalyticsTracker.kt, app/src/main/kotlin/org/grakovne/lissen/common/CrashReporter.kt
Introduced AnalyticsTracker and CrashReporter interfaces defining analytics and crash-reporting contracts.
FOSS flavor implementations
app/src/foss/kotlin/org/.../NoOpAnalyticsTracker.kt, app/src/foss/kotlin/org/.../NoOpCrashReporter.kt, app/src/foss/kotlin/org/.../AnalyticsModule.kt, app/src/foss/kotlin/org/.../ReportingModule.kt, app/src/foss/kotlin/org/.../AnalyticsExtensions.kt
Added no-op analytics/crash reporter implementations, DI bindings, and a no-op maskForAnalytics extension for the FOSS flavor.
Play flavor implementations
app/src/play/kotlin/org/.../MicrosoftClarityTracker.kt, app/src/play/kotlin/org/.../FirebaseCrashReporter.kt, app/src/play/kotlin/org/.../AnalyticsModule.kt, app/src/play/kotlin/org/.../ReportingModule.kt, app/src/play/kotlin/org/.../AnalyticsExtensions.kt
Added MicrosoftClarityTracker (queuing + consent-driven init), FirebaseCrashReporter wrapper, DI bindings, and maskForAnalytics delegating to clarityMask for Play builds.
Removed Clarity internals
app/src/main/kotlin/org/grakovne/lissen/analytics/ClarityComponent.kt, app/src/main/kotlin/org/grakovne/lissen/analytics/ClarityTracker.kt
Deleted ClarityComponent and ClarityTracker files (lifecycle-based init and direct Clarity wrapper removed).
DI / Reporting modules
app/src/foss/.../ReportingModule.kt, app/src/play/.../ReportingModule.kt
Added flavor-specific ReportingModule bindings mapping CrashReporter to no-op (FOSS) or Firebase implementation (Play).
Application & Crash handling
app/src/main/kotlin/org/grakovne/lissen/LissenApplication.kt, app/src/main/kotlin/org/grakovne/lissen/common/CrashHandler.kt
Injected CrashReporter into application and CrashHandler; simplified uncaught-exception handling and forwarded errors via CrashReporter.
Repositories & managers
app/src/main/kotlin/org/.../AuthRepository.kt, .../BookRepository.kt, .../ContentCachingManager.kt, .../MediaRepository.kt, .../ContentCachingManager.kt
Replaced ClarityTracker with AnalyticsTracker and switched Firebase Crashlytics usage to CrashReporter.recordException calls; updated constructor signatures accordingly.
Preferences & persistence
app/src/main/kotlin/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt
Added CrashReporter injection; replaced direct Firebase calls with CrashReporter and routed decryption exceptions to CrashReporter.
ViewModels
app/src/main/kotlin/org/grakovne/lissen/viewmodel/LibraryViewModel.kt, app/src/main/kotlin/org/grakovne/lissen/viewmodel/SettingsViewModel.kt
Swapped ClarityTracker/ClarityComponent for AnalyticsTracker, added isInitializing StateFlow in LibraryViewModel, and adapted refresh/init logic.
UI — masking & conditional UI
app/src/main/kotlin/org/.../LoginScreen.kt, .../LibrarySearchActionComposable.kt, .../ChapterSearchActionComposable.kt, .../CustomHeaderComposable.kt, .../LocalUrlComposable.kt, .../ServerSettingsComposable.kt
Replaced .clarityMask() with .maskForAnalytics() across composables; conditional rendering added for analytics consent and crash-toggle based on distribution.
UI — library / footer / scrollbar
app/src/main/kotlin/org/.../LibraryScreen.kt, .../LicenseFooterComposable.kt, .../VerticalScrollbar.kt, .../AdvancedSettingsComposable.kt
Added isInitializing usage in LibraryScreen, adjusted footer to include APP_NAME_SUFFIX and version prefix, removed Crashlytics calls from VerticalScrollbar, and hid crash-toggle for non-Play distribution.
Metadata
metadata/en-US/*
Updated app branding/title from "Lissen" to "Kahani", expanded short and full descriptions.

Sequence Diagram(s)

sequenceDiagram
  participant VM as ViewModel / UI
  participant Tracker as AnalyticsTracker (interface)
  participant Impl as MicrosoftClarityTracker (Play) / NoOp (FOSS)
  participant Clarity as Microsoft Clarity SDK
  participant Crash as CrashReporter -> FirebaseCrashlytics (Play) / NoOp (FOSS)

  VM->>Tracker: trackEvent(name, value)
  Tracker->>Impl: trackEvent(name, value)
  alt Impl not initialized (consent not given)
    Impl-->>Impl: enqueue event
  else consent accepted / initialized
    Impl->>Clarity: send event
  end

  VM->>Crash: recordException(Throwable)
  Crash->>Crash: delegate to FirebaseCrashlytics or no-op
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped from Clarity to a kinder track,
Two flavors now—one light, one with a pack,
Events queued, consent flips the switch to play,
Crash reports whisper, or quietly stay,
Kahani leaps forward—soft, clever, and slack.

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'FOSS Release Setup' directly describes the main change: configuring a FOSS (Free and Open Source Software) build variant alongside the existing Play Store variant, including new distribution flavors, no-op analytics/crash reporting implementations, and corresponding build configuration changes.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/compliance-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@SurjitSahoo SurjitSahoo changed the title Feature/compliance cleanup FOSS Release Setup Feb 14, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @.github/workflows/release.yml:
- Line 53: The release workflow currently hardcodes the release body as the
string "Update the Changelog" (the body field in the release step), which will
be used for every release; update the release job to either enable automatic
release notes by replacing the static body with the provider's dynamic option
(e.g., set generate_release_notes: true for the softprops/action-gh-release
step) or replace the literal with a meaningful placeholder or templated content
that incorporates changelog or commit info so releases are descriptive.

In
`@app/src/play/kotlin/org/grakovne/lissen/analytics/MicrosoftClarityTracker.kt`:
- Around line 32-41: The updateConsent method calls Clarity.pause() unguarded
which can fail if Clarity was never initialized; add an initialization guard:
introduce a private boolean (e.g., clarityInitialized) on the
MicrosoftClarityTracker class, set it to true immediately after calling
Clarity.initialize(...) in updateConsent (and false if you fully dispose), and
change the else branch to only call Clarity.pause() when clarityInitialized is
true (or alternatively check Clarity.isInitialized() if that API exists) so
pause is never invoked before initialization.
- Around line 17-26: trackEvent and setUser call Clarity.sendCustomEvent /
Clarity.setCustomUserId before Clarity.initialize() may finish; add an
initialization guard in MicrosoftClarityTracker so these methods never call
Clarity APIs until initialize completes: add an internal Boolean (e.g.,
clarityInitialized) set to true when updateConsent(true) completes its
Clarity.initialize() callback/promise, and in trackEvent(name, value) and
setUser(userId) check clarityInitialized first — if not initialized either queue
the event/userId to a small in-memory list to be flushed after initialization or
short-circuit and log/drop, but do not call Clarity.* methods; ensure
updateConsent(true) transitions clarityInitialized and flushes any queued items
after successful initialization.
🧹 Nitpick comments (4)
app/src/main/kotlin/org/grakovne/lissen/ui/screens/library/LibraryScreen.kt (1)

112-112: Consider using collectAsState() without an explicit initial value.

If isInitializing is a StateFlow, calling collectAsState() (no argument) will use the flow's current value as initial state, staying in sync with the ViewModel. Passing true explicitly means even if the ViewModel has already set it to false before composition, the UI will briefly see true and show the placeholder unnecessarily.

Proposed fix
-  val isInitializing by libraryViewModel.isInitializing.collectAsState(true)
+  val isInitializing by libraryViewModel.isInitializing.collectAsState()
build.gradle.kts (1)

3-17: String concatenation obfuscation is redundant given the existing variant system.

The codebase already uses Gradle product flavors (foss and play) with variant-specific dependency declarations (add("playImplementation", ...)), making the obfuscated strings unnecessary. The split strings ("com." + "google", "fire" + "base") appear to be kept primarily for F-Droid scanner compatibility (as noted in the code comment at line 152 of app/build.gradle.kts), but they add no functional benefit and hurt readability.

If F-Droid scanner compatibility requires the FOSS_REMOVE_START/END markers, document this requirement clearly in the README or build documentation. Otherwise, remove the obfuscation and rely solely on the variant system, which is cleaner and more maintainable:

if (!isFossBuild) {
  apply(plugin = "com.google.gms.google-services")
  apply(plugin = "com.google.firebase.crashlytics")
}
app/build.gradle.kts (2)

86-98: Hardcoded Clarity project ID in build config.

CLARITY_PROJECT_ID is embedded directly in the build file (Line 97). While Clarity project IDs are generally considered public (they end up in client-side code), for consistency with how other credentials are handled in this file (env vars / local.properties fallback), consider externalizing it.

Proposed change
     create("play") {
       dimension = "distribution"
       buildConfigField("String", "APP_NAME_SUFFIX", "\"\"")
       buildConfigField("String", "DISTRIBUTION", "\"play\"")
-      buildConfigField("String", "CLARITY_PROJECT_ID", "\"vc8bgk8nk9\"")
+      buildConfigField(
+        "String",
+        "CLARITY_PROJECT_ID",
+        "\"${System.getenv("CLARITY_PROJECT_ID") ?: localProperties.getProperty("CLARITY_PROJECT_ID") ?: ""}\""
+      )
     }

152-167: Plugin obfuscation via string concatenation is fragile.

The split-string approach ("com." + "google", "fire" + "base") to evade the F-Droid scanner is a known pattern but inherently brittle — any scanner update could defeat it. You already have FOSS_REMOVE_START/FOSS_REMOVE_END markers, which is the more robust approach since F-Droid's build system can strip those sections entirely.

Also, isFossBuild at Lines 153-154 is evaluated at configuration time. If a Gradle invocation includes both Foss and Play tasks (e.g., ./gradlew assembleFossDebug assemblePlayRelease), the Play build would fail because the plugins won't be applied. This is likely acceptable for your use case (separate CI jobs per flavor), but worth documenting.

Surjit Kumar Sahoo added 2 commits February 14, 2026 23:08
@SurjitSahoo SurjitSahoo merged commit 8553632 into main Feb 14, 2026
1 of 2 checks passed
@SurjitSahoo SurjitSahoo deleted the feature/compliance-cleanup branch February 14, 2026 17:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant