Skip to content

Fix #6365: Resolve intermittent Android Lint errors in app, testing, and utility - #6423

Open
Kishan8548 wants to merge 5 commits into
oppia:developfrom
Kishan8548:fix-6365-lint-checks-failing-intermittently
Open

Fix #6365: Resolve intermittent Android Lint errors in app, testing, and utility#6423
Kishan8548 wants to merge 5 commits into
oppia:developfrom
Kishan8548:fix-6365-lint-checks-failing-intermittently

Conversation

@Kishan8548

@Kishan8548 Kishan8548 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Explanation

Fixes #6365

Android Lint was intermittently failing (and consistently failing in --mode=full) due to NEW_API and WRONG_CONSTANT errors in three files.

Root Causes & Fixes

1. ControlButtonsViewModel.ktNEW_API (API 26)

java.util.Base64.getEncoder().encodeToString() requires API 26, but minSdk is 21. Replaced with android.util.Base64.encodeToString(..., Base64.NO_WRAP) which works on API 1+. NO_WRAP produces identical unwrapped output, preserving correct behaviour with the downstream chunked(80) logic.

2. OppiaParameterizedTestRunner.ktNEW_API (API 24)

Three calls required Java 8 / API 24:

  • field.getDeclaredAnnotation(...) → replaced with field.getAnnotation(...) (API 1+)
  • testClass.getDeclaredAnnotation(...) → replaced with testClass.getAnnotation(...) (API 1+)
  • method.getDeclaredAnnotationsByType(Iteration::class.java) → replaced with a custom Method.fetchIterations() extension function using standard Java reflection to handle both direct and repeatable container annotations without any API 24+ calls.

All @RequiresApi(Build.VERSION_CODES.N) annotations and unused Build/RequiresApi imports were removed.

3. MathTagHandler.kt & SvgPictureDrawable.ktWRONG_CONSTANT (false positive)

PixelFormat.TRANSPARENT and PixelFormat.TRANSLUCENT are valid return values for getOpacity() per the Android Drawable contract. Android Lint's WrongConstantDetector flags these as false positives due to a UAST annotation propagation bug. Added @Suppress("WrongConstant") with an explanatory comment in each case to silence the false-positive without masking any real error.

Verification

  • bazel run //scripts:android_lint_check -- $(pwd) --mode=fullTotal Issues: 0 — ANDROID LINT CHECK PASSED
  • bazel run //scripts:android_lint_check -- $(pwd) --checks=NewApi,WrongConstantTotal Issues: 0 — ANDROID LINT CHECK PASSED
  • MathTagHandlerTest → PASSED (23 tests)
  • ProfileAndDeviceIdFragmentTest → PASSED (51 tests)
  • ProfileNameValidatorTest → PASSED (17 tests)

Before & After Screenshots (for WRONG_CONSTANT usage trace)

As requested, here are before/after screenshots confirming that suppressing the WRONG_CONSTANT warnings in MathTagHandler.kt (LaTeX/math rendering) and SvgPictureDrawable.kt (SVG illustrations) causes zero visual regression:

LaTeX / Math rendering (MathTagHandler.kt)

Before After
Before math After math

SVG / Vector illustration rendering (SvgPictureDrawable.kt)

Before After
Before SVG After SVG

Note: The UI is intentionally identical before and after — this confirms that @Suppress("WrongConstant") is not silencing any real errors, only false-positive lint warnings.

Essential Checklist

  • The PR title starts with "Fix #bugnum: "
  • The explanation section above starts with "Fixes #bugnum: "
  • Any changes to scripts/assets files have their rationale included in the PR explanation.
  • The PR follows the style guide.
  • The PR does not contain any unnecessary code changes from Android Studio.
  • The PR is made from a branch that's not called "develop" and is up-to-date with "develop".
  • The PR is assigned to the appropriate reviewers.

Disclosure of LLM Usage

  • Did you use AI/LLMs when working on this PR? Yes
  • If yes, describe the extent AI was used: Used AI for brainstorming root causes of the intermittent lint failures, writing inline code comments, and writing the PR description.

…ing, and utility

- Replace java.util.Base64 (API 26+) with android.util.Base64 (API 1+)
  in ControlButtonsViewModel to fix NEW_API lint errors across all API levels.
- Replace getDeclaredAnnotation() (API 24+) with getAnnotation() (API 1+)
  in OppiaParameterizedTestRunner to fix NEW_API lint errors.
- Replace getDeclaredAnnotationsByType() (API 24+) with a custom
  Method.fetchIterations() helper using Java reflection for repeatable
  @iteration annotations, removing all @RequiresApi(N) annotations.
- Add @Suppress("WrongConstant") to getOpacity() in MathTagHandler and
  SvgPictureDrawable where PixelFormat constants are correctly used but
  trigger false-positive lint warnings due to UAST annotation bugs.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Results

Number of files assessed: 4
Overall Coverage: 84.21%
Coverage Analysis: PASS

Passing coverage

Files with passing code coverage
File Coverage Lines Hit Status Min Required
MathTagHandler.ktutility/src/main/java/org/oppia/android/util/parser/html/MathTagHandler.kt
84.21% 96 / 114 70%

Exempted coverage

Files exempted from coverage
File Exemption Reason
ControlButtonsViewModel.ktapp/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ControlButtonsViewModel.kt
This file is exempted from having a test file; skipping coverage check.
OppiaParameterizedTestRunner.kttesting/src/main/java/org/oppia/android/testing/junit/OppiaParameterizedTestRunner.kt
This file is exempted from having a test file; skipping coverage check.
SvgPictureDrawable.ktutility/src/main/java/org/oppia/android/util/parser/svg/SvgPictureDrawable.kt
This file is exempted from having a test file; skipping coverage check.

Refer test_file_exemptions.textproto for the comprehensive list of file exemptions and their required coverage percentages.

To learn more, visit the Oppia Android Code Coverage wiki page

@Kishan8548
Kishan8548 marked this pull request as ready for review September 3, 2026 02:21
Copilot AI lite review requested due to automatic review settings September 3, 2026 02:22
@Kishan8548
Kishan8548 requested review from a team as code owners September 3, 2026 02:22

Copilot AI 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.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Resolves intermittent Android Lint failures (especially in --mode=full) by removing NewApi usages below minSdk and suppressing known false-positive WrongConstant warnings for Drawable#getOpacity().

Changes:

  • Replaced Java Base64 usage with android.util.Base64 to avoid API 26 NewApi lint errors.
  • Updated parameterized test runner reflection logic to avoid API 24 annotation APIs.
  • Suppressed false-positive WrongConstant lint warnings for valid PixelFormat opacity values.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
utility/src/main/java/org/oppia/android/util/parser/svg/SvgPictureDrawable.kt Suppresses WrongConstant for PixelFormat.TRANSLUCENT in getOpacity().
utility/src/main/java/org/oppia/android/util/parser/html/MathTagHandler.kt Suppresses WrongConstant for PixelFormat.TRANSPARENT in getOpacity().
testing/src/main/java/org/oppia/android/testing/junit/OppiaParameterizedTestRunner.kt Removes API 24-only annotation reflection and replaces it with a custom iteration fetcher.
app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ControlButtonsViewModel.kt Uses android.util.Base64 to avoid API 26-only java.util.Base64.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +211 to +218
try {
val valueMethod = annotation.annotationClass.java.getMethod("value")
val result = valueMethod.invoke(annotation)
val containerIterations = (result as? Array<*>)?.filterIsInstance<Iteration>()
if (containerIterations != null) {
iterations.addAll(containerIterations)
}
} catch (ignored: Exception) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I agree with this concern. Could you please update this logic so that unexpected reflection errors are not silently ignored? @Kishan8548

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done!

@Neer-rn Neer-rn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hi @Kishan8548 please look at the co-pilot's comment and after you fix it please assign to manas or sandesh.

Comment on lines +211 to +218
try {
val valueMethod = annotation.annotationClass.java.getMethod("value")
val result = valueMethod.invoke(annotation)
val containerIterations = (result as? Array<*>)?.filterIsInstance<Iteration>()
if (containerIterations != null) {
iterations.addAll(containerIterations)
}
} catch (ignored: Exception) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I agree with this concern. Could you please update this logic so that unexpected reflection errors are not silently ignored? @Kishan8548

Replace try-catch with NoSuchMethodException (control flow) with a
firstOrNull check that explicitly verifies the method name, parameter
count, return type array, and component type equals Iteration.
Only invoke() is now wrapped in a try-catch, catching the specific
exceptions it can throw (IllegalAccessException,
InvocationTargetException) rather than silently ignoring all exceptions.
…tchIterations()

The cast is safe because returnType.componentType is verified to equal
Iteration::class.java before invoke() is called. The @Suppress is needed
to prevent the compiler from treating the unchecked cast warning as an error.
…pty() (API 1+)

Method.getParameterCount() requires API 26 which is above minSdk 21.
Replace with method.parameterTypes.isEmpty() using getParameterTypes()
which is available from API 1+.
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Results

Number of files assessed: 4
Overall Coverage: 84.21%
Coverage Analysis: PASS

Passing coverage

Files with passing code coverage
File Coverage Lines Hit Status Min Required
MathTagHandler.ktutility/src/main/java/org/oppia/android/util/parser/html/MathTagHandler.kt
84.21% 96 / 114 70%

Exempted coverage

Files exempted from coverage
File Exemption Reason
SvgPictureDrawable.ktutility/src/main/java/org/oppia/android/util/parser/svg/SvgPictureDrawable.kt
This file is exempted from having a test file; skipping coverage check.
ControlButtonsViewModel.ktapp/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ControlButtonsViewModel.kt
This file is exempted from having a test file; skipping coverage check.
OppiaParameterizedTestRunner.kttesting/src/main/java/org/oppia/android/testing/junit/OppiaParameterizedTestRunner.kt
This file is exempted from having a test file; skipping coverage check.

Refer test_file_exemptions.textproto for the comprehensive list of file exemptions and their required coverage percentages.

To learn more, visit the Oppia Android Code Coverage wiki page

@Kishan8548

Copy link
Copy Markdown
Collaborator Author

PTAL @manas-yu

@Neer-rn Neer-rn assigned manas-yu and adhiamboperes and unassigned Neer-rn and manas-yu Sep 4, 2026
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.

[BUG]: Lint Checks are failing Intermittently

5 participants