Skip to content

SDK-6066 [Android][ND] Phase 11: Kotlin-first ND classes + factory named args - #1069

Open
CTLalit wants to merge 1 commit into
SDK-6066-nd-phase10-refactorfrom
SDK-6066-nd-phase11-kotlin
Open

SDK-6066 [Android][ND] Phase 11: Kotlin-first ND classes + factory named args#1069
CTLalit wants to merge 1 commit into
SDK-6066-nd-phase10-refactorfrom
SDK-6066-nd-phase11-kotlin

Conversation

@CTLalit

@CTLalit CTLalit commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Part of epic SDK-6055 · ticket SDK-6066. Stacked on #1068. Kotlin-first cleanup of the ND fcap code (done on top of the stack, incremental).

Kotlin conversions (idiomatic, behavior-preserving)

  • NdFCManager.javaNdFCManager.kt@JvmStatic isFcapManaged (Java AnalyticsManager calls it), shownTodayCount as a val, when/runCatching/string-templates; renamed the private init(...)initDailyState(...) to avoid clashing with the Kotlin init {} block.
  • NdFcapGate.javaNdFcapGate.kt (internal object).
  • DisplayUnitResponse.javaDisplayUnitResponse.kt (internal; nullable ND deps + content-only secondary constructor for the send-test/preview path).

Idiom fixes

  • ControllerManager: import for NativeDisplayController instead of the fully-qualified name.
  • CleverTapFactory: named arguments on every Kotlin constructor call.
  • New Kotlin uses imports — no fully-qualified class names.

Named args — Java-constructor limitation

This module does not enable javaParameters, and Kotlin named arguments don't compile against Java constructors without it (every pre-existing named-arg call in the factory already targets a Kotlin class). So Java-defined constructors are intentionally left positional; named args were applied to all Kotlin constructions. Enabling javaParameters module-wide to cover the rest is a separate call.

Tests

  • Added EvalRulesTest, HeaderVoteListsTest, NativeDisplayControllerTest.
  • Extended EventQueueManagerTest to assert the ND-controller fan-out.
  • All pass, incl. the unchanged NdFCManagerTest / NdFcapGateTest / DisplayUnitResponseTest (proving the conversions preserve behavior) + EvaluationManagerTest, QueueHeaderBuilderTest, InAppControllerTest.

Coverage matrix — every touched class

Class Test
NdFCManager NdFCManagerTest
NdFcapGate NdFcapGateTest
DisplayUnitResponse DisplayUnitResponseTest
NativeDisplayController NativeDisplayControllerTest (new)
EvalRules EvalRulesTest (new)
HeaderVoteLists HeaderVoteListsTest (new)
NdStore NdStoreTest
NdEvaluationManager NdEvaluationManagerTest
EventQueueManager (ND fan-out) EventQueueManagerTest (extended)
QueueHeaderBuilder (ndtlc/ndmp) QueueHeaderBuilderTest
ControllerManager / CleverTapFactory (wiring) exercised via CleverTapFactoryTest + the above

🤖 Generated with Claude Code

…med args

Kotlin-first cleanup of the ND fcap code (done on top of the stack, incremental).

- Convert NdFCManager.java -> NdFCManager.kt (idiomatic; @JvmStatic isFcapManaged
  and shownTodayCount as a val for the Java/Kotlin callers).
- Convert NdFcapGate.java -> NdFcapGate.kt (internal object).
- Convert DisplayUnitResponse.java -> DisplayUnitResponse.kt (internal; nullable
  ND deps + content-only secondary ctor for the send-test path).
- ControllerManager: import NativeDisplayController instead of FQ name.
- CleverTapFactory: named arguments on every Kotlin constructor call. NOTE: Java
  constructors are left positional on purpose — this module does not enable
  javaParameters, so Kotlin named args don't compile against Java ctors.
- New code uses imports (no fully-qualified class names).
- Tests: add EvalRulesTest, HeaderVoteListsTest, NativeDisplayControllerTest;
  extend EventQueueManagerTest to assert the ND controller fan-out. Existing
  NdFCManagerTest / NdFcapGateTest / DisplayUnitResponseTest pass unchanged
  (behavior-preserving conversions).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • \b(master|main|develop)(_\w*)?

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7888dfb9-7b65-4248-8091-3a404f48dcde

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@shubhambjadhavar

shubhambjadhavar commented Aug 24, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@CTLalit
CTLalit marked this pull request as ready for review August 24, 2026 08:31

@clevertap-vision clevertap-vision Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

Summary

Phase 11 converts NdFCManager, NdFcapGate and DisplayUnitResponse from Java to Kotlin, adds named arguments to the Kotlin constructions in CleverTapFactory, and adds EvalRulesTest / HeaderVoteListsTest / NativeDisplayControllerTest. I diffed each converted file line-by-line against its deleted Java original: control flow, cap ordering, catch scopes, pref key derivation and the parseDisplayUnits cache-write-once path are all faithfully preserved, all named arguments resolve against real Kotlin parameter names, and every remaining Java caller (AnalyticsManager, CleverTapAPI, LoginController, ControllerManager) still links because Kotlin internal is public in bytecode.

📊 Visual Overview
flowchart LR
  subgraph Converted["Java → Kotlin (this PR)"]
    A["NdFCManager.kt<br/>@JvmStatic isFcapManaged<br/>shownTodayCount val<br/>init → initDailyState"]
    B["NdFcapGate.kt<br/>internal object<br/>returns List"]
    C["DisplayUnitResponse.kt<br/>internal + content-only ctor"]
  end
  F["CleverTapFactory.kt<br/>named args"] --> A
  F --> C
  C --> B --> A
  JavaCallers["AnalyticsManager.java<br/>CleverTapAPI.java"] -.-> A
  JavaCallers -.-> C
  T["New tests:<br/>EvalRules · HeaderVoteLists<br/>NativeDisplayController"] -.-> Converted
Loading

Verdict

APPROVE

The conversions are behavior-preserving apart from one benign, self-healing capture-semantics change in NdFCManager's async init; the remaining notes are readability/doc hygiene.


Automatic reviews run once per commit — the same commit isn't re-reviewed. Push a new commit to trigger a fresh review.

Reviewed by Vision AI


init {
executors.postAsyncSafelyTask<Unit>().execute("initNdFCManager") {
initDailyState(deviceId)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 SUGGESTION — Bug: This is the one place the conversion is not behavior-preserving. In the deleted Java the async task captured the constructor parameter deviceId (an effectively-final local), so the deferred init(...) always ran for the device id the manager was built with. Here deviceId inside the lambda resolves to the mutable property this.deviceId and is read when the task actually executes — if changeUser(newId) (LoginController.java:177) lands before the queued task runs, initDailyState runs twice for the new id and the original user's daily-counter reset is skipped for that day.

Impact is low (counters are keyed per device id and the reset self-heals on the next changeUser back to that user), but it's an unintended delta in a PR advertised as behavior-preserving. Pin the value:

Suggested change
initDailyState(deviceId)
init {
val initialDeviceId = deviceId
executors.postAsyncSafelyTask<Unit>().execute("initNdFCManager") {
initDailyState(initialDeviceId)
}
}

*/
internal object NdFcapGate {

private const val UNCAPPED = -1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 SUGGESTION — Code quality: UNCAPPED is the right name for the tlc/tdc defaults on lines 40-41, but it's also used as the optInt default for efc and excludeGlobalFCaps (lines 35-36), which are presence/boolean flags — -1 there means "absent", not "uncapped". It also silently duplicates NdFCManager.UNCAPPED, which this PR made private, so the two can now drift. Either add a separate ABSENT = -1 for the flag lookups, or make NdFCManager.UNCAPPED internal and reuse it for the two cap defaults.

* The ND dependencies are nullable: the send-test / push-preview path (see the content-only secondary
* constructor) carries a single display unit and no ND meta, so meta ingestion is skipped there.
*/
internal class DisplayUnitResponse(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 SUGGESTION — Code quality: The epic's living design doc still points at the pre-conversion filenames — docs/NativeDisplayFrequencyCaps.md:52 and :420 reference response/DisplayUnitResponse.java, and :143 references NdFCManager.getNdCounts/init (now initDailyState). Worth updating in this PR since it's the file-map readers use to navigate the ND channel.

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.

2 participants