Skip to content

Commit

Permalink
Fix part of #4044: Add KotliTeX integration (direct LaTeX rendering) (#…
Browse files Browse the repository at this point in the history
…4068)

## Explanation
Fix part of #4044

This PR introduces support for rendering raw LaTeX in the app using the KotliTeX (https://github.com/karino2/kotlitex) library which is necessary since users may input arbitrary math expressions that need to be pretty-rendered (using the math expression -> LaTeX generation support added in #4054).

This implementation is heavily based on #3194 and leverages a new fork (https://github.com/oppia/kotlitex) of KotliTeX based on @anandwana001's fork.

In order to keep the implementation simpler, this hangs the functionality onto the existing custom Oppia noninteractive math tag except it only renders LaTeX if there's no SVG filename to load (which will be the case for dynamically generated LaTeX). This approach has the advantage of the app always being able to fall back to LaTeX rendering in the unlikely scenario where content has a math tag without an SVG URL being present (or where the SVG fails to load which could allow potential fault tolerance when downloads support is fully implemented). These changes required several changes to KotliTeX itself which are outlined in more detail below.

Furthermore, it was observed that the rendering was noticeably slow on a Nexus 5X for repeated renders (think navigation back and forth between views with rendered LaTeX, or expanding/collapsing multiple answers). This didn't seem acceptable, so this PR also introduces a platform parameter for utilizing Glide to pre-render the LaTeX into a PNG bitmap so that it can cache it. This is not only more performant for repeated exposure and faster to render, but it also offloads the expensive rendering step to a background thread rather than blocking the main thread (see the videos in the UI section below for a before-and-after comparison).

### Details on changes to KotliTeX
A custom fork of KotliTeX (https://github.com/oppia/kotlitex) was needed to include the following changes:
- Support to be built on Bazel
- Lower the min sdk required by the library to match the app's
- A slight hack to ensure KotliTeX doesn't auto-fail for larger square roots (the library actually doesn't support them, so this workaround just stretches the smaller square root rather than leading to error text)
- A hack to expose the outer bounds of drawing requires by KotliTeX for its span (see caching details below for why)

Note that KotliTeX only supports a subset of LaTeX, but it seems to support everything the app needs for now (and it should be extensible by porting other portions of KaTeX as needed).

### Details on the rendering caching & positioning challenges
At a high-level, the approach to caching is to render the KotliTeX drawable to a bitmap that can be saved on-disk in a format that Glide can easily load on its own (PNG). While it's not ideal to perform the extra PNG conversion step, it's much more disk-friendly (and in the future, we could look into offloading just this part to another thread if needed). Each individual load is keyed on the exact raw LaTeX, the line height (up to 2 digits), and whether it's inline or block rendered (see later in this section for the distinction). If all three of these properties match, there should be a Glide cache hit and no need to re-render the LaTeX.

A couple of issues arose when trying to implement this:
- Despite us being able to expose the internal KotliTeX drawable (which wasn't by default), I was having a lot of difficulty getting the text alignment correct. Instead, it was much easier to rely on Android's internal text rendering by just rendering the span in a similar way to what TextView does.
- While the above worked, it led to another problem: figuring out how large the bitmap should be. The actual drawable bounds that KotliTeX computes is an underestimate (its ascent and descent go outside of these bounds), so an additional estimation is needed.
- Because we're now drawing the LaTeX as a bitmap in an ImageSpan, the text aligns the image either with its bottom line or its baseline. This introduces a vertical center alignment issue as shown below (the image with the red text shows one attempt at fixing this by eliminating unnecessary upper space by using a combination of vertical translation and negative bounds). Due to this quirk, we can't actually have whitespace below the text without modifying the text's font metrics (specifically the bottom/top/ascent/descent properties). #4170 is tracking this work. Note also that this is really only an issue for inline rendering which we're unlikely to use in the medium-term since all user-submitted answers will be rendered in block style which seems to work well.

Caching is demonstrably much more performant for large amounts of repetition, and it doesn't result in UI lag like direct rendering does since all rendering is offloaded to a background thread (see the videos below for the comparison).

### Details on testing
The tests are mainly focused around changes to the tag handler since the Glide portions can't be tested, and neither can the rendering routines (hence the exemptions). The fake image loader was updated to include support for the math-based rendering load requests.

An aside change was made to make the Robolectric library test-only (it's something that was noticed in passing during development, and isn't directly needed by this PR; it's just a nice-to-have).

The new interfaces are exempted from tests since they have no logic to verify (their implementations are generated by Dagger and verified at compile time). Furthermore, I didn't add any tests for the changes to the compute affected tests script since it's a bit difficult to test, and already quite an edge case. Please let me know if you have any concerns with this.

### Details on the race condition fix
KotliTeX seems to initialized shared state when its parsing the LaTeX, and this with multiple Glide threads can cause a race condition where the LaTeX fails to render. This has been fixed by leveraging coroutines (where we still try to maximize efficiency and parallelization), but it required exposing new injector pathways for the coroutine dispatchers and ``ConsoleLogger``.

### Details on the ComputeAffectedTests fix
This PR exposes an issue with the ComputeAffectedTests script wherein it can sometimes exceed the system's maximum argument length.  This is being worked around by partitioning values for longer Bazel queries into multiple calls and concatenating the results. I'm fairly certain this is functionally equivalent to the current implementation, and for the vast majority of future PRs only one partition will ever be used. There doesn't seem to be an alternative possible since this is a platform-specific Kernel restriction.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
- [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation.
- [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
For the most part, this PR doesn't actually affect users yet since this functionality isn't exposed (all LaTeX currently rendered in the app will utilize the existing SVG pipeline). The user-facing UI changes will be thoroughly documented in the PR that's planned for after this one since it'll be integrating all prior PRs along with the changes introduced here.

That being said, the "What is a Fraction?" test revision card has been updated to include rendered LaTeX text in order to demonstrate the functionality more closely. The following is how rendering looks (with bitmap caching):

![cached_latex_rendering](https://user-images.githubusercontent.com/12983742/153363770-ad1ad20a-c06e-4dae-a9ce-4253285f1adb.png)

Note that the above, when compared with non-cached rendering, demonstrates where some of the spacing issues mentioned above come in:

![direct_render_latex](https://user-images.githubusercontent.com/12983742/153363841-8318f520-0788-449f-9ea8-276c8898981e.png)

Here's the image showing the bounds when trying to force the drawable downward:

![difficult_alignment_inline_math](https://user-images.githubusercontent.com/12983742/153363908-568dca88-6c67-4b11-98f6-fed0e17d56b8.png)

This video shows how long a bunch of LaTeX expressions takes to render with direct rendering:

https://user-images.githubusercontent.com/12983742/153363983-4ff99db5-52da-4360-9630-cc98397dc07b.mp4

Versus the performance of caching for the same experience (note this occurred with a completely clean local app cache):

https://user-images.githubusercontent.com/12983742/153364178-3066d6ce-2a85-45c8-83fe-c0378a786023.mp4

### Notes on rendering approach

Note that rendering during cache time involves a three step process:
1. Approximate the outer bounding volume by tracking all draw calls needed for a given LaTeX expression (these bounds are used to create the bitmap that will store the cacheable render)
2. Render the LaTeX to a canvas with dimensions larger than the approximated bounding (since it's possible for the rendering to exceed these estimates--see notes below)
3. Crop the bitmap to the smallest bounds needed to encapsulate all of the pixels

(1) is needed because KotliTeX actually incorrect computes bounds (it sometimes over and underestimates). This isn't as apparent when directly rendering because Android will handle text expanding past the space KotliTeX expects since it's rendered in-line. I spent some time trying to debug this within KotliTeX but I couldn't make much headway. It may be worth looking into this in the future to see if we can reliably calculate the bounds within KotliTeX (since it will likely be much more performant than the solution used here). Seeing KotliTeX's debug lines makes this clearer (where it thinks the bounds are relative to glyphs; note that this screenshot is using rendering without caching):

![kotlitex_wrong_bounds](https://user-images.githubusercontent.com/12983742/155447759-5e21ba68-8cec-496a-843b-db0109278625.png)

(2) is needed because the methodology used for (1) is inexact. It's actually really difficult to estimate exactly where the final glyph pixels will be rendered for a given glyph since KotliTeX is directly handling positioning (rather than allowing Android to compute x/y coordinates based on nearby characters to account for tracking, kerning, etc.). The dimension approximation for (1) is much closer to correct than KotliTeX's internal bounds calculations, however it tends to slightly underapproximate the bounds. To counteract this, the app uses a bitmap that's 2x what's actually estimated to be needed and offsets rendering into that bitmap such that 50% of the estimated size is a margin around the drawing area. This provides a large buffer to account for incorrect bounds calculations. The app then crops this to the edges of filled pixels so that the smallest bitmap is used to represent the final rendered equation. While this uses a lot more memory than should actually be required for rendering, it guarantees perfect results. See:

Before (notice the over-sized LaTeX, and that some expressions are partially cut-off from the right and above, or have extra space below):

| ![latex_rendering_cutoff_problem1](https://user-images.githubusercontent.com/12983742/155447426-0662f705-7ec3-4422-a45f-f36e42948968.png) | ![latex_rendering_cutoff_problem2](https://user-images.githubusercontent.com/12983742/155447432-35ba68d6-b05b-4a91-82ce-7af03a47851b.png) |
|------|------|

After (with size estimation & croppingl this also includes a fix in UrlImageParser to ensure that LaTeX images are never auto-resized when rendering in block mode since it distorts them, and that they are properly centered):

| ![latex_rendering_cutoff_fix1](https://user-images.githubusercontent.com/12983742/155447617-86eb68bc-9c8b-4483-b630-bba14b2c0cb7.png) | ![latex_rendering_cutoff_fix2](https://user-images.githubusercontent.com/12983742/155447622-fea0e52f-0dc1-4cac-b673-6a102fe5f430.png) |
|------|------|


Commit history:

* Add KDocs & test exemptions.

* Lint fixes.

* Remove temporary TODOs.

* Add tests.

* Split StringToFractionParser.

This is a temporary change that will be finished upstream (since there's
an earlier PR that's a better fit for this change).

* Address reviewer comments + other stuff.

This also fixes a typo and incorrectly ordered exemptions list I noticed
during development of downstream PRs.

* Move StringExtensions & fraction parsing.

This splits fraction parsing between UI & utility components.

* Address reviewer comments.

* Alphabetize test exemptions.

* Fix typo & add regex check.

The new regex check makes it so that all parameterized testing can be
more easily tracked by the Android TL.

* Add missing KDocs.

* Post-merge cleanups.

Also, fix text file exemption ordering.

* Add new test for negation with math symbol.

* Post-merge fixes.

* Add KDocs.

Also, add new regex exemption for new parameterized tests in this
branch.

* Refactor & simplify real ext impl.

Also, fix/clarify some KDocs.

* Lint fixes.

* Simplify operation list converter a lot.

This inlines three recursive operations to be done during the actual
computation to simplify the overall converter complexity (and to make
determining the test matrix easier).

* Prepare for new tests.

* Remove the ComparableOperationList wrapper.

* Change parameterized method delimiter.

* Use utility directly in test.

* Post-merge fixes.

This adjusts for the removal of ComparableOperationList (i.e. no wrapper
proto).

* Add first round of tests.

This includes fixes to the converter itself as it wasn't distributing
both product inversions and negation correctly in several cases. Tests
should now be covering these cases.

* Finish initial test suite.

Still needs to be cleaned up, but after converter refactoring attempts.

* Simplify operation sorting comparators.

* Remove old tests.

* Add remaining missing tests.

* KDocs & test exemption.

* Renames & lint fixes.

* Post-merge fixes.

* Add tests.

* KDocs + exemptions.

Also, clean up polynomial sorting.

* Lint fixes.

* Post-merge fixes.

Also, mark methods/classes that need tests.

* Add extension tests.

* Add classifier tests.

* Use more intentional epsilons for float comparing.

* Treat en-dash as a subtraction symbol.

* Add explicit platform selection for paramerized.

This adds explicit platform selection support rather than it being
automatic based on deps. While less flexible for shared tests, this
offers better control for tests that don't want to to use Robolectric
for local tests.

This also adds a JUnit-only test runner, and updates MathTokenizerTest
to use it (which led to an almost 40x decrease in runtime).

* Exemption fixes.

Also, fix name for the AndroidJUnit4 runner.

* Remove failing test.

* Fix unary expression precedence.

Also, use ParameterizedJunitTestRunner for MathExpressionParserTest.

* Fixes & add more test cases.

* Post-merge fixes & test changes.

Also, update RealExtensionsTest to use the faster JUnit runner.

* Use utility directly in LaTeX tests.

* Post-merge fixes.

Also, update ExpressionToComparableOperationConverterTest to use the
fast JUnit-only runner.

* Post-merge fixes.

Also, update PolynomialExtensionsTest to use fast JUnit-only runner.

* Post-merge fixes.

Also, update float interval per new tests.

* Lint & other check fixes.

* Replace deprecated term.

* Post-merge fixes.

* Add full test suites for alg exp classifiers.

* Lint & static check fixes.

* Fix test on Gradle.

* Fix test for Gradle.

* Add tests for math equations.

And, post-merge fixes.

* Static check & lint fixes.

* Post-merge fixes.

Verified CI checks & all unit tests are passing.

* Split up tests.

Also, adds dedicated BUILD.bazel file for new test.

* Add missing test in Bazel, and fix it.

* Correct order for genrule.

* Add full test suite.

* Clean up + KDocs + exemption.

* Lint fixes.

* Post-merge fix.

* Cache KotliTeX renders.

Directly rendering LaTeX through KotliTeX is way too slow, so this
introduces a custom flow through Glide that computes a PNG for the LaTeX
on a background thread and then caches it as part of Glide's cache to
speed up re-renders of the LaTeX. We may need to manage/prune the cache
over time, but for now we'll just rely on Glide's internal behaviors.

This also documents some new tests that should be added, but it's not
comprehensive.

* Add tests, docs, and exemptions.

* Update to fixed version of KotliTeX.

The newer version correctly computes the bounds for rendered LaTeX.

* Lint fixes.

* Add new dependency licenses.

This isn't done yet (some of the licenses still need to be fixed).

* Fix license links.

Note that the kxml one was tricky since its Maven entry says it's
licensed under BSD and CC0 1.0, and its SourceForge link says the same
plus LGPL 2.0. However, the source code and GitHub version of the
project license it under MIT, and this seems to predate the others so
it seems like the most correct license to use in this case and the one
that we're using to represent the dependency.

* Fix Gradle build.

This uses a version of KotliTeX that builds correctly on Jitpack for Gradle,
and fixes the StaticLayout creation to use an alignment constant that
builds on Gradle (I'm not sure why there's a difference here between
Gradle & Bazel, but the previous constant isn't part of the enum per
official Android docs).

* Create the math drawable synchronously.

This requires exposing new injectors broadly in the app since the math
model loader doesn't have access to the dependency injection graph
directly.

* Remove new deps from Maven list.

They were incorrectly pulled in by KotliTeX.

* Add argument partitioning.

This fixes cases where argument calls may be very large and fail to
execute due to exceeding system limitations.

* Make allowance for empty cases to fix tests.

These tests correspond to real scenarios.

* Lint fixes.

* Address reviewer comment.

Clarifies the documentation in the test runner around parameter
injection.

* Fix broken build.

* Fix broken build post-merge.

* Fix broken post-merge classifier.

* Address reviewer comment.

* Post-merge build fixes.

* Post-merge build fixes for new classifiers.

* Post-merge build fixes.

* Correct reference document link.

* Ensure LaTeX isn't stretched or cut-off.

The comments in-code have much more specifics on the approach.

* Add and fix missing test (was broken on Gradle).

* Post-merge fix.

* More post-merge fixes.

* Fix TODO comment.

* Post-merge fix.

* Update KotliTeX version.

This version doesn't have debug drawing enabled.
  • Loading branch information
BenHenning authored Mar 27, 2022
1 parent adbce54 commit 4173714
Show file tree
Hide file tree
Showing 38 changed files with 1,351 additions and 134 deletions.
9 changes: 9 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ git_repository(
remote = "https://github.com/oppia/androidsvg",
)

# A custom fork of KotliTeX that removes resources artifacts that break the build, and updates the
# min target SDK version to be compatible with Oppia.
git_repository(
name = "kotlitex",
commit = "6b7db8ff9e0f4a70bdaa25f482143e038fd0c301",
remote = "https://github.com/oppia/kotlitex",
shallow_since = "1647554845 -0700",
)

bind(
name = "databinding_annotation_processor",
actual = "//tools/android:compiler_annotation_processor",
Expand Down
2 changes: 2 additions & 0 deletions app/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,9 @@ kt_android_library(
"//utility/src/main/java/org/oppia/android/util/parser/image:image_parsing_module",
# TODO(#2432): Replace debug_module with prod_module when building the app in prod mode.
"//utility/src/main/java/org/oppia/android/util/networking:debug_module",
"//utility/src/main/java/org/oppia/android/util/logging:console_logger_injector_provider",
"//utility/src/main/java/org/oppia/android/util/statusbar:status_bar_color",
"//utility/src/main/java/org/oppia/android/util/threading:dispatcher_injector_provider",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ package org.oppia.android.app.application
import org.oppia.android.app.translation.AppLanguageApplicationInjector
import org.oppia.android.domain.locale.LocaleApplicationInjector
import org.oppia.android.util.data.DataProvidersInjector
import org.oppia.android.util.logging.ConsoleLoggerInjector
import org.oppia.android.util.system.OppiaClockInjector
import org.oppia.android.util.threading.DispatcherInjector

/** Injector for application-level dependencies that can't be directly injected where needed. */
interface ApplicationInjector :
DataProvidersInjector,
AppLanguageApplicationInjector,
OppiaClockInjector,
LocaleApplicationInjector
LocaleApplicationInjector,
DispatcherInjector,
ConsoleLoggerInjector
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ import org.oppia.android.domain.locale.LocaleApplicationInjector
import org.oppia.android.domain.locale.LocaleApplicationInjectorProvider
import org.oppia.android.util.data.DataProvidersInjector
import org.oppia.android.util.data.DataProvidersInjectorProvider
import org.oppia.android.util.logging.ConsoleLoggerInjector
import org.oppia.android.util.logging.ConsoleLoggerInjectorProvider
import org.oppia.android.util.system.OppiaClockInjector
import org.oppia.android.util.system.OppiaClockInjectorProvider
import org.oppia.android.util.threading.DispatcherInjector
import org.oppia.android.util.threading.DispatcherInjectorProvider

/** Provider for [ApplicationInjector]. The application context will implement this interface. */
interface ApplicationInjectorProvider :
DataProvidersInjectorProvider,
AppLanguageApplicationInjectorProvider,
OppiaClockInjectorProvider,
LocaleApplicationInjectorProvider {
LocaleApplicationInjectorProvider,
DispatcherInjectorProvider,
ConsoleLoggerInjectorProvider {
fun getApplicationInjector(): ApplicationInjector

override fun getDataProvidersInjector(): DataProvidersInjector = getApplicationInjector()
Expand All @@ -25,4 +31,8 @@ interface ApplicationInjectorProvider :
override fun getOppiaClockInjector(): OppiaClockInjector = getApplicationInjector()

override fun getLocaleApplicationInjector(): LocaleApplicationInjector = getApplicationInjector()

override fun getDispatcherInjector(): DispatcherInjector = getApplicationInjector()

override fun getConsoleLoggerInjector(): ConsoleLoggerInjector = getApplicationInjector()
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule
import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule
import org.oppia.android.util.parser.image.GlideImageLoaderModule
import org.oppia.android.util.parser.image.ImageParsingModule
import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING
import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VALUE
import org.oppia.android.util.platformparameter.CacheLatexRendering
import org.oppia.android.util.platformparameter.EnableLanguageSelectionUi
import org.oppia.android.util.platformparameter.PlatformParameterSingleton
import org.oppia.android.util.platformparameter.PlatformParameterValue
import org.oppia.android.util.platformparameter.SPLASH_SCREEN_WELCOME_MSG_DEFAULT_VALUE
import org.oppia.android.util.platformparameter.SYNC_UP_WORKER_TIME_PERIOD_IN_HOURS_DEFAULT_VALUE
Expand Down Expand Up @@ -654,6 +658,15 @@ class OptionsFragmentTest {
fun provideEnableLanguageSelectionUi(): PlatformParameterValue<Boolean> {
return PlatformParameterValue.createDefaultParameter(forceEnableLanguageSelectionUi)
}

@Provides
@CacheLatexRendering
fun provideCacheLatexRendering(
platformParameterSingleton: PlatformParameterSingleton
): PlatformParameterValue<Boolean> {
return platformParameterSingleton.getBooleanPlatformParameter(CACHE_LATEX_RENDERING)
?: PlatformParameterValue.createDefaultParameter(CACHE_LATEX_RENDERING_DEFAULT_VALUE)
}
}

// TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,62 @@ class HtmlParserTest {
onView(withId(R.id.test_html_content_text_view)).perform(click())
}

@Test
fun testHtmlContent_withMathTag_missingFileName_inlineMode_loadsNonMathModeKotlitexMathSpan() {
val htmlParser = htmlParserFactory.create(
resourceBucketName,
entityType = "",
entityId = "",
imageCenterAlign = true,
)
activityRule.scenario.runWithActivity {
val textView: TextView = it.findViewById(R.id.test_html_content_text_view)
val htmlResult: Spannable = htmlParser.parseOppiaHtml(
"<oppia-noninteractive-math render-type=\"inline\" math_content-with-value=\"{" +
"&amp;quot;raw_latex&amp;quot;:&amp;quot;\\\\frac{2}{5}&amp;quot;}\">" +
"</oppia-noninteractive-math>",
textView,
supportsLinks = true,
supportsConceptCards = true
)
textView.text = htmlResult
}

// The rendering mode should be inline for this render type.
val loadedInlineImages = testGlideImageLoader.getLoadedMathDrawables()
assertThat(loadedInlineImages).hasSize(1)
assertThat(loadedInlineImages.first().rawLatex).isEqualTo("\\frac{2}{5}")
assertThat(loadedInlineImages.first().useInlineRendering).isTrue()
}

@Test
fun testHtmlContent_withMathTag_missingFileName_blockMode_loadsMathModeKotlitexMathSpan() {
val htmlParser = htmlParserFactory.create(
resourceBucketName,
entityType = "",
entityId = "",
imageCenterAlign = true,
)
activityRule.scenario.runWithActivity {
val textView: TextView = it.findViewById(R.id.test_html_content_text_view)
val htmlResult: Spannable = htmlParser.parseOppiaHtml(
"<oppia-noninteractive-math render-type=\"block\" math_content-with-value=\"{" +
"&amp;quot;raw_latex&amp;quot;:&amp;quot;\\\\frac{2}{5}&amp;quot;}\">" +
"</oppia-noninteractive-math>",
textView,
supportsLinks = true,
supportsConceptCards = true
)
textView.text = htmlResult
}

// The rendering mode should be non-inline for this render type.
val loadedInlineImages = testGlideImageLoader.getLoadedMathDrawables()
assertThat(loadedInlineImages).hasSize(1)
assertThat(loadedInlineImages.first().rawLatex).isEqualTo("\\frac{2}{5}")
assertThat(loadedInlineImages.first().useInlineRendering).isFalse()
}

@Test
fun testHtmlContent_withMathTag_loadsTextSvg() {
val htmlParser = htmlParserFactory.create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule
import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule
import org.oppia.android.util.parser.image.GlideImageLoaderModule
import org.oppia.android.util.parser.image.ImageParsingModule
import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING
import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VALUE
import org.oppia.android.util.platformparameter.CacheLatexRendering
import org.oppia.android.util.platformparameter.EnableLanguageSelectionUi
import org.oppia.android.util.platformparameter.PlatformParameterSingleton
import org.oppia.android.util.platformparameter.PlatformParameterValue
import org.oppia.android.util.platformparameter.SPLASH_SCREEN_WELCOME_MSG_DEFAULT_VALUE
import org.oppia.android.util.platformparameter.SYNC_UP_WORKER_TIME_PERIOD_IN_HOURS_DEFAULT_VALUE
Expand Down Expand Up @@ -251,6 +255,15 @@ class OptionsFragmentTest {
fun provideEnableLanguageSelectionUi(): PlatformParameterValue<Boolean> {
return PlatformParameterValue.createDefaultParameter(forceEnableLanguageSelectionUi)
}

@Provides
@CacheLatexRendering
fun provideCacheLatexRendering(
platformParameterSingleton: PlatformParameterSingleton
): PlatformParameterValue<Boolean> {
return platformParameterSingleton.getBooleanPlatformParameter(CACHE_LATEX_RENDERING)
?: PlatformParameterValue.createDefaultParameter(CACHE_LATEX_RENDERING_DEFAULT_VALUE)
}
}

// TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them.
Expand Down
2 changes: 1 addition & 1 deletion domain/src/main/assets/GJ2rLXRKD5hw_1.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"page_contents": {
"subtitled_html": {
"content_id": "content",
"html": "<p>Description of subtopic is here.</p>.<p>This is sample subtopic with dummy content related to Fractions.<p>Fractions represent equal parts of a whole or a collection. Fraction of a whole: When we divide a whole into equal parts, each part is a fraction of the whole. A fraction has two parts. The number on the top of the line is called the numerator. It tells how many equal parts of the whole or collection are taken. The number below the line is called the denominator. It shows the total divisible number of equal parts the whole into or the total number of equal parts which are there in a collection."
"html": "<p>Description of subtopic is here.</p>.<p>This is sample subtopic with dummy content related to Fractions: <oppia-noninteractive-math render-type=\"inline\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;\\\\frac{\\\\frac{1}{6}}{\\\\frac{1}{2}}&amp;quot;}\"></oppia-noninteractive-math> A lot more text that hopefully wraps to the next line in order to see whether the fraction correctly breaks subsequent text lines since we definitely don't want it to overlap since then it would most certainly not be readable in the least.<p>Fractions represent equal parts of a whole or a collection. Fraction of a whole: When we divide a whole into equal parts, each part is a fraction of the whole. A fraction has two parts. The number on the top of the line is called the numerator. It tells how many equal parts of the whole or collection are taken. The number below the line is called the denominator. It shows the total divisible number of equal parts the whole into or the total number of equal parts which are there in a collection. Also, here's the equation for a line using in-line LaTeX: <oppia-noninteractive-math render-type=\"inline\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;y=mx+b&amp;quot;}\" /><p><oppia-noninteractive-math render-type=\"block\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;(x + 1)(x - 2) = \\\\frac{(x ^ {3} + 2x ^ {2} - 5x - 6)}{(x + b)}&amp;quot;}\" /><p><oppia-noninteractive-math render-type=\"block\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;y=mx+b&amp;quot;}\" /><p><oppia-noninteractive-math render-type=\"block\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;\\\\sqrt{\\\\frac{\\\\frac{\\\\frac{\\\\frac{\\\\frac{\\\\frac{\\\\frac{\\\\frac{1}{2}}{3}}{4}}{5}}{6}}{7}}{8}}{9}}&amp;quot;}\" />"
},
"recorded_voiceovers": {
"voiceovers_mapping": {
Expand Down
2 changes: 1 addition & 1 deletion domain/src/main/assets/GJ2rLXRKD5hw_1.textproto
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
subtopic_title: "What is a Fraction?"
page_contents {
html: "<p>Description of subtopic is here.</p>.<p>This is sample subtopic with dummy content related to Fractions.<p>Fractions represent equal parts of a whole or a collection. Fraction of a whole: When we divide a whole into equal parts, each part is a fraction of the whole. A fraction has two parts. The number on the top of the line is called the numerator. It tells how many equal parts of the whole or collection are taken. The number below the line is called the denominator. It shows the total divisible number of equal parts the whole into or the total number of equal parts which are there in a collection."
html: "<p>Description of subtopic is here.</p>.<p>This is sample subtopic with dummy content related to Fractions: <oppia-noninteractive-math render-type=\"inline\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;\\\\frac{\\\\frac{1}{6}}{\\\\frac{1}{2}}&amp;quot;}\"></oppia-noninteractive-math> A lot more text that hopefully wraps to the next line in order to see whether the fraction correctly breaks subsequent text lines since we definitely don't want it to overlap since then it would most certainly not be readable in the least.<p>Fractions represent equal parts of a whole or a collection. Fraction of a whole: When we divide a whole into equal parts, each part is a fraction of the whole. A fraction has two parts. The number on the top of the line is called the numerator. It tells how many equal parts of the whole or collection are taken. The number below the line is called the denominator. It shows the total divisible number of equal parts the whole into or the total number of equal parts which are there in a collection. Also, here's the equation for a line using in-line LaTeX: <oppia-noninteractive-math render-type=\"inline\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;y=mx+b&amp;quot;}\" /><p><oppia-noninteractive-math render-type=\"block\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;(x + 1)(x - 2) = \\\\frac{(x ^ {3} + 2x ^ {2} - 5x - 6)}{(x + b)}&amp;quot;}\" /><p><oppia-noninteractive-math render-type=\"block\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;y=mx+b&amp;quot;}\" /><p><oppia-noninteractive-math render-type=\"block\" math_content-with-value=\"{&amp;quot;raw_latex&amp;quot;:&amp;quot;\\\\sqrt{\\\\frac{\\\\frac{\\\\frac{\\\\frac{\\\\frac{\\\\frac{\\\\frac{\\\\frac{1}{2}}{3}}{4}}{5}}{6}}{7}}{8}}{9}}&amp;quot;}\" />"
content_id: "content"
}
recorded_voiceover {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package org.oppia.android.domain.platformparameter

import dagger.Module
import dagger.Provides
import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING
import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VALUE
import org.oppia.android.util.platformparameter.CacheLatexRendering
import org.oppia.android.util.platformparameter.ENABLE_LANGUAGE_SELECTION_UI_DEFAULT_VALUE
import org.oppia.android.util.platformparameter.EnableLanguageSelectionUi
import org.oppia.android.util.platformparameter.LEARNER_STUDY_ANALYTICS
Expand Down Expand Up @@ -56,4 +59,13 @@ class PlatformParameterModule {
return platformParameterSingleton.getBooleanPlatformParameter(LEARNER_STUDY_ANALYTICS)
?: PlatformParameterValue.createDefaultParameter(LEARNER_STUDY_ANALYTICS_DEFAULT_VALUE)
}

@Provides
@CacheLatexRendering
fun provideCacheLatexRendering(
platformParameterSingleton: PlatformParameterSingleton
): PlatformParameterValue<Boolean> {
return platformParameterSingleton.getBooleanPlatformParameter(CACHE_LATEX_RENDERING)
?: PlatformParameterValue.createDefaultParameter(CACHE_LATEX_RENDERING_DEFAULT_VALUE)
}
}
5 changes: 5 additions & 0 deletions scripts/assets/test_file_exemptions.textproto
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,8 @@ exempted_file_path: "utility/src/main/java/org/oppia/android/util/gcsresource/Gc
exempted_file_path: "utility/src/main/java/org/oppia/android/util/locale/OppiaBidiFormatter.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/locale/OppiaLocale.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/logging/ConsoleLogger.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/logging/ConsoleLoggerInjector.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/logging/ConsoleLoggerInjectorProvider.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/logging/EventLogger.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/logging/ExceptionLogger.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/logging/LogLevel.kt"
Expand Down Expand Up @@ -751,6 +753,7 @@ exempted_file_path: "utility/src/main/java/org/oppia/android/util/parser/image/R
exempted_file_path: "utility/src/main/java/org/oppia/android/util/parser/image/RepositoryModelLoader.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/parser/image/TestGlideImageLoader.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/parser/image/TextPictureDrawable.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/parser/math/MathBitmapModelLoader.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/parser/svg/BlockPictureDrawable.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/parser/svg/BlockSvgDrawableTranscoder.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/parser/svg/ScalableVectorGraphic.kt"
Expand All @@ -769,4 +772,6 @@ exempted_file_path: "utility/src/main/java/org/oppia/android/util/system/OppiaCl
exempted_file_path: "utility/src/main/java/org/oppia/android/util/threading/BackgroundDispatcher.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/threading/BlockingDispatcher.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/threading/ConcurrentCollections.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/threading/DispatcherInjector.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/threading/DispatcherInjectorProvider.kt"
exempted_file_path: "utility/src/main/java/org/oppia/android/util/threading/DispatcherModule.kt"
Loading

0 comments on commit 4173714

Please sign in to comment.