diff --git a/WORKSPACE b/WORKSPACE index 7b21505c587..0c20dcd36a1 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -125,9 +125,9 @@ git_repository( # to correctly size in-line SVGs (such as those needed for LaTeX-based math expressions). git_repository( name = "androidsvg", - commit = "4bc1d26412f0fb9fd4ef263fa93f6a64f4d4dbcf", + commit = "1265eb1087056cf3fc2e10442e5545bc65c109ce", remote = "https://github.com/oppia/androidsvg", - shallow_since = "1647295507 -0700", + shallow_since = "1686302944 -0700", ) git_repository( diff --git a/app/src/test/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt b/app/src/test/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt index b7515853e05..91339129701 100644 --- a/app/src/test/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt +++ b/app/src/test/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt @@ -562,16 +562,16 @@ class ListItemLeadingMarginSpanTest { textView.layout ) - assertThat(shadowCanvas.getDrawnCircle(0).centerX).isWithin(1e-5f).of(944.0f) + assertThat(shadowCanvas.getDrawnCircle(0).centerX).isWithin(1e-5f).of(926.0f) assertThat(shadowCanvas.getDrawnCircle(0).centerY).isWithin(1e-5f).of(48.0f) - assertThat(shadowCanvas.getDrawnCircle(1).centerX).isWithin(1e-5f).of(878.0f) + assertThat(shadowCanvas.getDrawnCircle(1).centerX).isWithin(1e-5f).of(860.0f) assertThat(shadowCanvas.getDrawnCircle(1).centerY).isWithin(1e-5f).of(139.0f) - assertThat(shadowCanvas.getDrawnCircle(2).centerX).isWithin(1e-5f).of(878.0f) + assertThat(shadowCanvas.getDrawnCircle(2).centerX).isWithin(1e-5f).of(860.0f) assertThat(shadowCanvas.getDrawnCircle(2).centerY).isWithin(1e-5f).of(225.0f) - assertThat(shadowCanvas.getDrawnCircle(3).centerX).isWithin(1e-5f).of(944.0f) + assertThat(shadowCanvas.getDrawnCircle(3).centerX).isWithin(1e-5f).of(926.0f) assertThat(shadowCanvas.getDrawnCircle(3).centerY).isWithin(1e-5f).of(397.0f) } diff --git a/utility/build.gradle b/utility/build.gradle index 568eac1841b..52164f42b34 100644 --- a/utility/build.gradle +++ b/utility/build.gradle @@ -82,7 +82,7 @@ dependencies { 'androidx.appcompat:appcompat:1.0.2', 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha03', 'androidx.work:work-runtime-ktx:2.4.0', - 'com.github.oppia:androidsvg:4bc1d26412f0fb9fd4ef263fa93f6a64f4d4dbcf', + 'com.github.oppia:androidsvg:1265eb1087056cf3fc2e10442e5545bc65c109ce', 'com.github.oppia:kotlitex:43139c140833c7120f351d63d74b42c253d2b213', 'com.github.bumptech.glide:glide:4.11.0', 'com.google.dagger:dagger:2.24', diff --git a/utility/src/main/java/org/oppia/android/util/parser/html/ImageTagHandler.kt b/utility/src/main/java/org/oppia/android/util/parser/html/ImageTagHandler.kt index 39ccbe5e9e4..27bdc52cb37 100644 --- a/utility/src/main/java/org/oppia/android/util/parser/html/ImageTagHandler.kt +++ b/utility/src/main/java/org/oppia/android/util/parser/html/ImageTagHandler.kt @@ -48,12 +48,12 @@ class ImageTagHandler( ) } } else consoleLogger.e("ImageTagHandler", "Failed to parse image tag") - if (contentDescription != null) { + if (!contentDescription.isNullOrBlank()) { val spannableBuilder = SpannableStringBuilder(contentDescription) spannableBuilder.setSpan( contentDescription, - /* start= */ 0, - /* end= */ contentDescription.length, + /* start = */ 0, + /* end = */ contentDescription.length, Spannable.SPAN_INCLUSIVE_EXCLUSIVE ) output.replace(openIndex, output.length, spannableBuilder) diff --git a/utility/src/main/java/org/oppia/android/util/parser/html/ListItemLeadingMarginSpan.kt b/utility/src/main/java/org/oppia/android/util/parser/html/ListItemLeadingMarginSpan.kt index 36bce0e79df..de1c028aed2 100644 --- a/utility/src/main/java/org/oppia/android/util/parser/html/ListItemLeadingMarginSpan.kt +++ b/utility/src/main/java/org/oppia/android/util/parser/html/ListItemLeadingMarginSpan.kt @@ -46,6 +46,7 @@ sealed class ListItemLeadingMarginSpan : LeadingMarginSpan { private val isRtl by lazy { displayLocale.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL } + private val clipBounds by lazy { Rect() } override fun drawLeadingMargin( canvas: Canvas, @@ -69,8 +70,14 @@ sealed class ListItemLeadingMarginSpan : LeadingMarginSpan { val bulletDrawRadius = bulletRadius.toFloat() val indentedX = parentAbsoluteLeadingMargin + spacingBeforeBullet - val bulletStartX = (if (isRtl) canvas.width - indentedX - 1 else indentedX).toFloat() - val bulletCenterX = bulletStartX + bulletDrawRadius + val bulletCenterLtrX = indentedX + bulletDrawRadius + val bulletCenterX = if (isRtl) { + // See https://stackoverflow.com/a/21845993/3689782 for 'right' property exclusivity. + val maxDrawX = if (canvas.getClipBounds(clipBounds)) { + clipBounds.right - 1 + } else canvas.width - 1 + maxDrawX - bulletCenterLtrX + } else bulletCenterLtrX val bulletCenterY = (top + bottom) / 2f when (indentationLevel) { 0 -> { @@ -90,7 +97,7 @@ sealed class ListItemLeadingMarginSpan : LeadingMarginSpan { val rectSize = bulletDiameter.toFloat() canvas.drawRect( RectF().apply { - left = bulletStartX + left = bulletCenterX right = left + rectSize this.top = bulletCenterY - bulletDrawRadius this.bottom = this.top + rectSize diff --git a/utility/src/test/java/org/oppia/android/util/parser/html/ImageTagHandlerTest.kt b/utility/src/test/java/org/oppia/android/util/parser/html/ImageTagHandlerTest.kt index f13ca551998..17219b230f7 100644 --- a/utility/src/test/java/org/oppia/android/util/parser/html/ImageTagHandlerTest.kt +++ b/utility/src/test/java/org/oppia/android/util/parser/html/ImageTagHandlerTest.kt @@ -55,6 +55,21 @@ private const val IMAGE_TAG_WITHOUT_ALT_VALUE_MARKUP = "" +private const val IMAGE_TAG_WITH_EMPTY_ALT_VALUE_MARKUP = + "" + +private const val IMAGE_TAG_WITH_EMPTY_STRING_ALT_VALUE_MARKUP = + "" + +private const val IMAGE_TAG_WITH_SPACE_ONLY_ALT_VALUE_MARKUP = + "" + /** Tests for [ImageTagHandler]. */ @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -138,6 +153,51 @@ class ImageTagHandlerTest { assertThat(parsedHtml.first().isObjectReplacementCharacter()).isFalse() } + @Test + fun testParseHtml_withEmptyImageCardMarkup_hasNoReadableText() { + val parsedHtml = + CustomHtmlContentHandler.fromHtml( + html = IMAGE_TAG_WITH_EMPTY_ALT_VALUE_MARKUP, + imageRetriever = mockImageRetriever, + customTagHandlers = tagHandlersWithImageTagSupport + ) + + // If the alt text is present but empty, then only the image control character should show. + val parsedHtmlStr = parsedHtml.toString() + assertThat(parsedHtmlStr).hasLength(1) + assertThat(parsedHtmlStr.first().isObjectReplacementCharacter()).isTrue() + } + + @Test + fun testParseHtml_withEmptyImageCardMarkupString_hasNoReadableText() { + val parsedHtml = + CustomHtmlContentHandler.fromHtml( + html = IMAGE_TAG_WITH_EMPTY_STRING_ALT_VALUE_MARKUP, + imageRetriever = mockImageRetriever, + customTagHandlers = tagHandlersWithImageTagSupport + ) + + // If the alt text is present but empty, then only the image control character should show. + val parsedHtmlStr = parsedHtml.toString() + assertThat(parsedHtmlStr).hasLength(1) + assertThat(parsedHtmlStr.first().isObjectReplacementCharacter()).isTrue() + } + + @Test + fun testParseHtml_withSpaceOnlyImageCardMarkup_hasNoReadableText() { + val parsedHtml = + CustomHtmlContentHandler.fromHtml( + html = IMAGE_TAG_WITH_SPACE_ONLY_ALT_VALUE_MARKUP, + imageRetriever = mockImageRetriever, + customTagHandlers = tagHandlersWithImageTagSupport + ) + + // If the alt text is present but only spaces, then the image control character should show. + val parsedHtmlStr = parsedHtml.toString() + assertThat(parsedHtmlStr).hasLength(1) + assertThat(parsedHtmlStr.first().isObjectReplacementCharacter()).isTrue() + } + @Test fun testParseHtml_withImageCardMarkup_missingFilename_doesNotIncludeImageSpan() { val parsedHtml =