Skip to content

Conversation

SuriyaKannimuthu
Copy link

@SuriyaKannimuthu SuriyaKannimuthu commented Oct 15, 2025

Resolved app crashes caused by invalid or malformed color strings by introducing safe color parsing in ColorUtils. Now defaults to a fallback color instead of throwing exceptions.

Summary by CodeRabbit

  • Bug Fixes

    • In-app notifications now validate and gracefully fall back when color values are missing or malformed, preventing visual errors and ensuring consistent display.
  • New Features

    • Added safer color-parsing utilities that provide default colors for invalid inputs and a null-safe validation path so themes and notifications render reliably.

@francispereira
Copy link

francispereira commented Oct 15, 2025

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

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

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

Copy link
Contributor

coderabbitai bot commented Oct 15, 2025

Walkthrough

Adds safe color-parsing utilities and integrates them into CTInAppNotification deserialization and JSON parsing to validate or fallback color string values.

Changes

Cohort / File(s) Summary
Color utilities
clevertap-core/src/main/java/com/clevertap/android/sdk/utils/ColorUtils.kt
Adds String?.toColorIntOrDefault(@ColorInt defaultColor: Int = GraphicColor.WHITE): Int, String?.toValidColorOrFallback(fallback: String): String, and object Color with @ColorInt parseColor(...) overloads. Implements null/blank checks and catches IllegalArgumentException/StringIndexOutOfBoundsException to return fallbacks.
In-app notification color validation
clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/CTInAppNotification.kt
Replaces direct fallbacks with toValidColorOrFallback() when reconstructing titleColor, backgroundColor, and messageColor from Parcel and JSON, ensuring validated or fallback color strings are used.

Sequence Diagram(s)

sequenceDiagram
  participant CT as CTInAppNotification
  participant Utils as ColorUtils.Color / extensions
  participant Parser as android.graphics.Color

  CT->>Utils: toValidColorOrFallback(colorString, fallback)
  alt colorString null/blank
    Utils-->>CT: return fallback
  else
    Utils->>Parser: toColorInt(colorString)
    alt parse success
      Parser-->>Utils: colorInt
      Utils-->>CT: return original colorString
    else parse error
      Parser-->>Utils: throws
      Utils-->>CT: return fallback
    end
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "[SDK - 5236] [Android] Parse color fallback" directly and accurately summarizes the main change introduced in this pull request. The phrase "Parse color fallback" clearly refers to the new safe color parsing utilities added to ColorUtils.kt with fallback behavior, as well as the modifications to CTInAppNotification.kt that apply this new functionality. The title is concise, specific, and avoids vague terms or noise. A teammate reviewing the commit history would immediately understand that this change addresses color parsing with fallback mechanisms to handle invalid color strings.
✨ 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 task/android/sdk-5236

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1581380 and 4d7400b.

📒 Files selected for processing (1)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/utils/ColorUtils.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/utils/ColorUtils.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: lint-staticChecks-test-build

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.

Copy link
Contributor

@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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d5d477 and 5f72cf3.

📒 Files selected for processing (11)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppBaseFullNativeFragment.kt (4 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppBasePartialNativeFragment.kt (2 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeCoverFragment.kt (3 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeCoverImageFragment.kt (2 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeFooterFragment.kt (3 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeHalfInterstitialFragment.kt (3 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeHalfInterstitialImageFragment.kt (2 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeHeaderFragment.kt (3 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeInterstitialFragment.kt (3 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeInterstitialImageFragment.kt (2 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/utils/ColorUtils.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-09T13:27:32.141Z
Learnt from: CTLalit
PR: CleverTap/clevertap-android-sdk#846
File: clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeHalfInterstitialImageFragment.kt:20-143
Timestamp: 2025-07-09T13:27:32.141Z
Learning: In the CleverTap Android SDK, CTLalit considers the CTInAppNativeHalfInterstitialImageFragment class as legacy code and prefers not to refactor it, even when the code has complex methods that could benefit from restructuring.

Applied to files:

  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeFooterFragment.kt
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeCoverFragment.kt
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppBasePartialNativeFragment.kt
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeHalfInterstitialFragment.kt
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeCoverImageFragment.kt
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeInterstitialImageFragment.kt
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeInterstitialFragment.kt
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeHeaderFragment.kt
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeHalfInterstitialImageFragment.kt
📚 Learning: 2025-06-18T09:47:50.616Z
Learnt from: vasct
PR: CleverTap/clevertap-android-sdk#831
File: clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeCoverImageFragment.kt:55-59
Timestamp: 2025-06-18T09:47:50.616Z
Learning: In CleverTap Android SDK in-app notification fragments, the `isHideCloseButton` flag has counterintuitive logic where `!isHideCloseButton` sets visibility to GONE and `isHideCloseButton` sets visibility to VISIBLE. This is intentional behavior preserved from the original Java implementation and should not be "fixed" during Kotlin conversion.

Applied to files:

  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppBasePartialNativeFragment.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: lint-staticChecks-test-build
🔇 Additional comments (7)
clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeInterstitialFragment.kt (2)

30-30: LGTM! Safe color parsing import added.

The import of toColorIntOrDefault correctly replaces the previous toColorInt import, enabling defensive color parsing throughout this fragment.


87-87: Validate BLACK fallback UX
The toColorIntOrDefault() extension defaults to Color.BLACK on failure. Verify that using black as the fallback for backgrounds, titles, and messages doesn’t produce unreadable combinations (e.g., black text on a black background).

clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppBaseFullNativeFragment.kt (1)

16-16: LGTM! Button color parsing now safely defaults.

The import and usage of toColorIntOrDefault() for button text color, background color, and border color provide appropriate fallback behavior, preventing crashes from invalid color values in button configurations.

Also applies to: 29-29, 47-47, 66-66

clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeCoverImageFragment.kt (1)

13-13: LGTM! Background color parsing now safely defaults.

The import and usage of toColorIntOrDefault() for the cover image background color appropriately prevents crashes from invalid color values.

Also applies to: 31-31

clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppBasePartialNativeFragment.kt (1)

19-19: LGTM! Button color parsing now safely defaults.

The import and usage of toColorIntOrDefault() for button text and background colors provide appropriate fallback behavior for partial fragment buttons (header/footer), preventing crashes from invalid color values.

Also applies to: 115-116

clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeHeaderFragment.kt (1)

16-16: LGTM! Header color parsing now safely defaults.

The import and usage of toColorIntOrDefault() for background, title, and message colors appropriately prevent crashes from invalid color values in header fragments.

Also applies to: 32-32, 60-60, 64-64

clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/fragment/CTInAppNativeFooterFragment.kt (1)

16-16: LGTM! Footer color parsing now safely defaults.

The import and usage of toColorIntOrDefault() for background, title, and message colors appropriately prevent crashes from invalid color values in footer fragments. The implementation is consistent with the pattern applied across all other in-app fragments in this PR.

Also applies to: 31-31, 59-59, 63-63

Copy link

github-actions bot commented Oct 15, 2025

Code Coverage Debug

Metric (instruction) Coverage Threshold Δ Coverage Status
Overall 64.57% 75.0% -0.1%
Changed Files 82.56% 75.0% -3.33%
Report Coverage (O/Ch) Threshold (O/Ch) Δ Coverage (O/Ch) Status (O/Ch)
clevertap-core 65.51% / 108.15% 75.0% / 75.0% -0.11% / +22.26% ❌/✅
clevertap-pushtemplates 43.3% / 0.0% 75.0% / 75.0% 0.0% / 0.0% ❌/✅

@CTLalit CTLalit changed the title [SDK - 5236] [Android] Handled invalid colour values in In-App display flow -> it causes app crash [SDK - 5236] [Android] Parse color fallback Oct 15, 2025
fl.findViewById<RelativeLayout>(R.id.interstitial_image_relative_layout)

relativeLayout.setBackgroundColor(inAppNotification.backgroundColor.toColorInt())
relativeLayout.setBackgroundColor(inAppNotification.backgroundColor.toColorIntOrDefault())
Copy link
Contributor

Choose a reason for hiding this comment

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

The default colour will be black here @SuriyaKannimuthu ?

Ideally it should be white

Will it be simpler to validate all the colours in CTInAppNotification.kt while parsing from the JSONObject

Copy link
Author

Choose a reason for hiding this comment

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

Hi @Anush-Shand

I assigned black as the default based on a default assumption (since background is usually white). It’s configurable, so we can change it to white if needed. - Will change to white.

We can handle color validation directly in CTInAppNotification.kt while parsing the JSON — I initially thought of doing it that way since it’s simpler.
However, creating a utility to handle color parsing helps avoid manual null and format checks.
With the utility in place, all invalid or malformed color values are safely handled in one place. in future we can use the util method ".toColorIntOrDefault()"

White color default
* @return The parsed color integer or [defaultColor] if parsing fails.
*/
@ColorInt
fun String?.toColorIntOrDefault(@ColorInt defaultColor: Int = Color.WHITE): Int {
Copy link
Contributor

Choose a reason for hiding this comment

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

@suriya

Making default colour as white for everywhere won't work since the text might have default colour as black

Also we have defined default colours in CTInappNotification.kt
Again defining separate default colours in the extension function will be duplicate. In the future if I have to change the default colour I will have to change it in 2 places. A developer might miss this and hence could be erroneous

Copy link
Author

Choose a reason for hiding this comment

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

Okay @Anush-Shand, Doing as per the comments and making changes considering the default colors in CTInappNotification.kt.

Copy link
Contributor

@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: 1

🧹 Nitpick comments (2)
clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/CTInAppNotification.kt (2)

196-199: Color validation in parcel deserialization looks good.

The pattern (parcel.readString() ?: existingColor).toValidColorOrFallback(existingColor) correctly handles null values and validates color strings, falling back to the existing default colors (BLACK for text, WHITE for background).

Minor: Kotlin convention is to omit semicolons at the end of statements.

-        titleColor = (parcel.readString() ?: titleColor).toValidColorOrFallback(titleColor);
-        backgroundColor = (parcel.readString() ?: backgroundColor).toValidColorOrFallback(backgroundColor);
+        titleColor = (parcel.readString() ?: titleColor).toValidColorOrFallback(titleColor)
+        backgroundColor = (parcel.readString() ?: backgroundColor).toValidColorOrFallback(backgroundColor)
-        messageColor = (parcel.readString() ?: messageColor).toValidColorOrFallback(messageColor);
+        messageColor = (parcel.readString() ?: messageColor).toValidColorOrFallback(messageColor)

356-372: Color validation in JSON deserialization looks good.

The validation logic correctly ensures that invalid color strings from JSON fall back to the existing defaults, preventing crashes from malformed data.

Minor: Remove trailing semicolons per Kotlin conventions.

-            backgroundColor = jsonObject.optString(Constants.KEY_BG, backgroundColor).toValidColorOrFallback(backgroundColor);
+            backgroundColor = jsonObject.optString(Constants.KEY_BG, backgroundColor).toValidColorOrFallback(backgroundColor)
-                titleColor = titleObject.optString(Constants.KEY_COLOR, titleColor).toValidColorOrFallback(titleColor);
+                titleColor = titleObject.optString(Constants.KEY_COLOR, titleColor).toValidColorOrFallback(titleColor)
-                messageColor = msgObject.optString(Constants.KEY_COLOR, messageColor).toValidColorOrFallback(messageColor);
+                messageColor = msgObject.optString(Constants.KEY_COLOR, messageColor).toValidColorOrFallback(messageColor)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3fbac2c and 1581380.

📒 Files selected for processing (2)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/CTInAppNotification.kt (4 hunks)
  • clevertap-core/src/main/java/com/clevertap/android/sdk/utils/ColorUtils.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-24T12:08:40.196Z
Learnt from: Anush-Shand
PR: CleverTap/clevertap-android-sdk#872
File: clevertap-pushtemplates/src/main/java/com/clevertap/android/pushtemplates/styles/Style.kt:39-45
Timestamp: 2025-09-24T12:08:40.196Z
Learning: The Style.kt setNotificationBuilderBasics method in the CleverTap Android SDK push templates is legacy code. When flagging potential safety issues like NPE from Html.fromHtml(pt_title) with null values or Color.parseColor crashes with blank strings, the team prefers to defer such improvements rather than modify legacy code during feature-focused PRs.

Applied to files:

  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/CTInAppNotification.kt
📚 Learning: 2025-04-28T08:41:36.134Z
Learnt from: CTLalit
PR: CleverTap/clevertap-android-sdk#797
File: clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/CTInAppNotification.java:184-185
Timestamp: 2025-04-28T08:41:36.134Z
Learning: In the CleverTap Android SDK, parcels in the CTInAppNotification class exist only in memory and are not persisted, so backward compatibility concerns between different SDK versions are not applicable for these parcels.

Applied to files:

  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/CTInAppNotification.kt
📚 Learning: 2025-07-09T13:28:25.364Z
Learnt from: CTLalit
PR: CleverTap/clevertap-android-sdk#846
File: clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/CTInAppNotification.kt:422-432
Timestamp: 2025-07-09T13:28:25.364Z
Learning: CTLalit considers the CTInAppNotification class in the CleverTap Android SDK as legacy code and prefers not to refactor it, even when the code has potential improvements like media validation logic that could be more efficient.

Applied to files:

  • clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/CTInAppNotification.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: lint-staticChecks-test-build
🔇 Additional comments (3)
clevertap-core/src/main/java/com/clevertap/android/sdk/inapp/CTInAppNotification.kt (1)

14-14: LGTM!

The import of toValidColorOrFallback is appropriate for the safe color validation added in this PR.

clevertap-core/src/main/java/com/clevertap/android/sdk/utils/ColorUtils.kt (2)

24-39: LGTM!

The toColorIntOrDefault extension function provides robust null-safe color parsing with appropriate exception handling for invalid formats.


58-69: LGTM!

The toValidColorOrFallback extension function correctly validates color strings and returns the fallback when validation fails. This is the key function used by CTInAppNotification to prevent crashes from malformed color data.

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.

3 participants