Skip to content

Commit

Permalink
Merge branch 'develop' into left-align-policy-text-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
TanishMoral11 authored Feb 18, 2025
2 parents 37efff5 + d7eaad8 commit 058259d
Show file tree
Hide file tree
Showing 30 changed files with 963 additions and 204 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/wiki.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Deploy to Wiki

on:
workflow_dispatch:
pull_request:
paths:
- 'wiki/**'
Expand Down Expand Up @@ -36,6 +37,8 @@ jobs:
bazel run //scripts:wiki_table_of_contents_check -- ${GITHUB_WORKSPACE}
wiki-deploy:
permissions:
contents: write
runs-on: ${{ matrix.os }}
strategy:
matrix:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.fragment.app.FragmentManager
import org.oppia.android.app.model.ImageWithRegions
import org.oppia.android.app.model.UserAnswerState
import org.oppia.android.app.shim.ViewBindingShim
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.app.utility.ClickableAreasImage
import org.oppia.android.app.utility.OnClickableAreaClickedListener
import org.oppia.android.app.view.ViewComponentFactory
Expand Down Expand Up @@ -46,6 +47,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor(
@Inject lateinit var machineLocale: OppiaLocale.MachineLocale
@Inject lateinit var accessibilityService: AccessibilityService
@Inject lateinit var imageLoader: ImageLoader
@Inject lateinit var resourceHandler: AppLanguageResourceHandler

private lateinit var entityId: String
private lateinit var overlayView: FrameLayout
Expand All @@ -64,8 +66,8 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor(
maybeInitializeClickableAreas()
}

fun setUserAnswerState(userAnswerrState: UserAnswerState) {
this.userAnswerState = userAnswerrState
fun setUserAnswerState(userAnswerState: UserAnswerState) {
this.userAnswerState = userAnswerState
}

fun setEntityId(entityId: String) {
Expand Down Expand Up @@ -118,7 +120,8 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor(
::entityId.isInitialized &&
::imageUrl.isInitialized &&
::onRegionClicked.isInitialized &&
::overlayView.isInitialized
::overlayView.isInitialized &&
::resourceHandler.isInitialized
) {
loadImage()

Expand All @@ -129,7 +132,8 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor(
bindingInterface,
isAccessibilityEnabled = accessibilityService.isScreenReaderEnabled(),
clickableAreas,
userAnswerState
userAnswerState,
resourceHandler
)
areasImage.addRegionViews()
performAttachment(areasImage)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.oppia.android.app.player.state

import android.app.Application
import android.content.Context
import android.view.LayoutInflater
import android.view.View
Expand Down Expand Up @@ -91,7 +92,18 @@ import org.oppia.android.databinding.SubmittedHtmlAnswerItemBinding
import org.oppia.android.databinding.TextInputInteractionItemBinding
import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.util.accessibility.AccessibilityService
import org.oppia.android.util.logging.ConsoleLogger
import org.oppia.android.util.parser.html.CUSTOM_CONCEPT_CARD_TAG
import org.oppia.android.util.parser.html.CUSTOM_IMG_TAG
import org.oppia.android.util.parser.html.CUSTOM_LIST_LI_TAG
import org.oppia.android.util.parser.html.CUSTOM_LIST_OL_TAG
import org.oppia.android.util.parser.html.CUSTOM_LIST_UL_TAG
import org.oppia.android.util.parser.html.CUSTOM_MATH_TAG
import org.oppia.android.util.parser.html.ConceptCardTagHandler
import org.oppia.android.util.parser.html.HtmlParser
import org.oppia.android.util.parser.html.ImageTagHandler
import org.oppia.android.util.parser.html.LiTagHandler
import org.oppia.android.util.parser.html.MathTagHandler
import org.oppia.android.util.threading.BackgroundDispatcher
import javax.inject.Inject

Expand Down Expand Up @@ -145,7 +157,9 @@ class StatePlayerRecyclerViewAssembler private constructor(
private val hasConversationView: Boolean,
private val resourceHandler: AppLanguageResourceHandler,
private val translationController: TranslationController,
private var userAnswerState: UserAnswerState
private var userAnswerState: UserAnswerState,
private val consoleLogger: ConsoleLogger,
private val conceptCardTagHandlerFactory: ConceptCardTagHandler.Factory,
) : HtmlParser.CustomOppiaTagActionListener {
/**
* A list of view models corresponding to past view models that are hidden by default. These are
Expand Down Expand Up @@ -180,6 +194,25 @@ class StatePlayerRecyclerViewAssembler private constructor(
}
}

private val displayLocale = resourceHandler.getDisplayLocale()
private val customTagHandlers = mapOf(
CUSTOM_LIST_LI_TAG to LiTagHandler(context, displayLocale),
CUSTOM_LIST_UL_TAG to LiTagHandler(context, displayLocale),
CUSTOM_LIST_OL_TAG to LiTagHandler(context, displayLocale),
CUSTOM_IMG_TAG to ImageTagHandler(consoleLogger),
CUSTOM_CONCEPT_CARD_TAG to ConceptCardTagHandler(
conceptCardTagHandlerFactory.createConceptCardLinkClickListener(),
consoleLogger
),
// Pick an arbitrary line height since rendering doesn't actually happen.
CUSTOM_MATH_TAG to MathTagHandler(
consoleLogger,
context.assets,
10.0f,
false,
context.applicationContext as Application,
)
)
private val isSplitView = ObservableField<Boolean>(false)

override fun onConceptCardLinkClicked(view: View, skillId: String) {
Expand Down Expand Up @@ -350,7 +383,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
gcsEntityId,
hasConversationView,
isSplitView.get()!!,
playerFeatureSet.conceptCardSupport
playerFeatureSet.conceptCardSupport,
customTagHandlers
)
}
}
Expand Down Expand Up @@ -913,7 +947,9 @@ class StatePlayerRecyclerViewAssembler private constructor(
private val translationController: TranslationController,
private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory,
private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory,
private val userAnswerState: UserAnswerState
private val userAnswerState: UserAnswerState,
private val consoleLogger: ConsoleLogger,
private val conceptCardTagHandlerFactory: ConceptCardTagHandler.Factory,
) {

private val adapterBuilder: BindableAdapter.MultiTypeBuilder<StateItemViewModel,
Expand Down Expand Up @@ -1400,7 +1436,9 @@ class StatePlayerRecyclerViewAssembler private constructor(
hasConversationView,
resourceHandler,
translationController,
userAnswerState
userAnswerState,
consoleLogger,
conceptCardTagHandlerFactory
)
if (playerFeatureSet.conceptCardSupport) {
customTagListener.proxyListener = assembler
Expand All @@ -1420,7 +1458,9 @@ class StatePlayerRecyclerViewAssembler private constructor(
private val resourceHandler: AppLanguageResourceHandler,
private val translationController: TranslationController,
private val multiAdapterBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory,
private val singleAdapterFactory: BindableAdapter.SingleTypeBuilder.Factory
private val singleAdapterFactory: BindableAdapter.SingleTypeBuilder.Factory,
private val consoleLogger: ConsoleLogger,
private val conceptCardTagHandlerFactory: ConceptCardTagHandler.Factory,
) {
/**
* Returns a new [Builder] for the specified GCS resource bucket information for loading
Expand All @@ -1446,7 +1486,9 @@ class StatePlayerRecyclerViewAssembler private constructor(
translationController,
multiAdapterBuilderFactory,
singleAdapterFactory,
userAnswerState
userAnswerState,
consoleLogger,
conceptCardTagHandlerFactory
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,35 @@
package org.oppia.android.app.player.state.itemviewmodel

import android.text.Spannable
import android.text.SpannableStringBuilder
import org.oppia.android.util.parser.html.CustomHtmlContentHandler

/** [StateItemViewModel] for content-card state. */
class ContentViewModel(
val htmlContent: CharSequence,
val gcsEntityId: String,
val hasConversationView: Boolean,
val isSplitView: Boolean,
val supportsConceptCards: Boolean
val supportsConceptCards: Boolean,
val customTagHandlers: Map<String, CustomHtmlContentHandler.CustomTagHandler>
) : StateItemViewModel(ViewType.CONTENT) {

private val underscoreRegex = Regex("(?<=\\s|[,.;?!])_{3,}(?=\\s|[,.;?!])")
private val replacementText = "Blank"

/** Returns content description by extracting text from [htmlContent]. */
fun getContentDescription(): String {
val contentDescription = CustomHtmlContentHandler.getContentDescription(
htmlContent.toString(),
imageRetriever = null,
customTagHandlers = customTagHandlers
)
return replaceRegexWithBlank(contentDescription)
}

/**
* Replaces "2+ underscores, with space/punctuation on both sides" in the input text with a
* replacement string "blank", returning a Spannable.
* Adjusts offsets to handle text length changes during replacements.
*/
fun replaceRegexWithBlank(inputText: CharSequence): Spannable {
val spannableStringBuilder = SpannableStringBuilder(inputText)
val matches = underscoreRegex.findAll(inputText)
var lengthOffset = 0

for (match in matches) {
val matchStart = match.range.first + lengthOffset
val matchEnd = match.range.last + 1 + lengthOffset
spannableStringBuilder.replace(matchStart, matchEnd, replacementText)

// Adjust offset due to change in length (difference between old and new text length)
lengthOffset += replacementText.length - (matchEnd - matchStart)
}
return spannableStringBuilder
}
private fun replaceRegexWithBlank(inputText: CharSequence): String =
underscoreRegex.replace(inputText, replacementText)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,38 @@ package org.oppia.android.app.player.state.itemviewmodel

import androidx.databinding.ObservableBoolean
import org.oppia.android.app.model.SubtitledHtml
import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.viewmodel.ObservableViewModel
import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.util.parser.html.CustomHtmlContentHandler

/** [ObservableViewModel] for MultipleChoiceInput values or ItemSelectionInput values. */
class SelectionInteractionContentViewModel(
val htmlContent: SubtitledHtml,
val hasConversationView: Boolean,
private val itemIndex: Int,
private val selectionInteractionViewModel: SelectionInteractionViewModel,
val isEnabled: ObservableBoolean
val isEnabled: ObservableBoolean,
val customTagHandlers: Map<String, CustomHtmlContentHandler.CustomTagHandler>,
private val writtenTranslationContext: WrittenTranslationContext,
private val translationController: TranslationController,
) : ObservableViewModel() {
var isAnswerSelected = ObservableBoolean()

/** Returns content description by extracting text from [htmlContent]. */
fun getContentDescription(): String {
val contentSubtitledHtml =
translationController.extractString(
htmlContent, writtenTranslationContext
)
return CustomHtmlContentHandler.getContentDescription(
contentSubtitledHtml,
imageRetriever = null,
customTagHandlers = customTagHandlers
)
}

/** Handles item click by updating the selection state based on user interaction. */
fun handleItemClicked() {
val isCurrentlySelected = isAnswerSelected.get()
val shouldNowBeSelected =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiv
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.app.viewmodel.ObservableArrayList
import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.util.logging.ConsoleLogger
import org.oppia.android.util.parser.html.CUSTOM_IMG_TAG
import org.oppia.android.util.parser.html.CustomHtmlContentHandler
import org.oppia.android.util.parser.html.ImageTagHandler
import javax.inject.Inject

/** Corresponds to the type of input that should be used for an item selection interaction view. */
Expand Down Expand Up @@ -52,7 +56,8 @@ class SelectionInteractionViewModel private constructor(
val writtenTranslationContext: WrittenTranslationContext,
private val translationController: TranslationController,
private val resourceHandler: AppLanguageResourceHandler,
userAnswerState: UserAnswerState
userAnswerState: UserAnswerState,
consoleLogger: ConsoleLogger
) : StateItemViewModel(ViewType.SELECTION_INTERACTION), InteractionAnswerHandler {
private val interactionId: String = interaction.id

Expand Down Expand Up @@ -81,9 +86,20 @@ class SelectionInteractionViewModel private constructor(
ObservableBoolean(true)
}
}
val choiceItems: ObservableList<SelectionInteractionContentViewModel> =
computeChoiceItems(choiceSubtitledHtmls, hasConversationView, this, enabledItemsList)
private val customTagHandlers = mapOf<String, CustomHtmlContentHandler.CustomTagHandler>(
CUSTOM_IMG_TAG to ImageTagHandler(consoleLogger)
)

val choiceItems: ObservableList<SelectionInteractionContentViewModel> =
computeChoiceItems(
choiceSubtitledHtmls,
hasConversationView,
this,
enabledItemsList,
this@SelectionInteractionViewModel.writtenTranslationContext,
translationController,
customTagHandlers
)
private var pendingAnswerError: String? = null
private val isAnswerAvailable = ObservableField(false)
val errorMessage = ObservableField<String>("")
Expand Down Expand Up @@ -287,7 +303,8 @@ class SelectionInteractionViewModel private constructor(
/** Implementation of [StateItemViewModel.InteractionItemFactory] for this view model. */
class FactoryImpl @Inject constructor(
private val translationController: TranslationController,
private val resourceHandler: AppLanguageResourceHandler
private val resourceHandler: AppLanguageResourceHandler,
private val consoleLogger: ConsoleLogger
) : InteractionItemFactory {
override fun create(
entityId: String,
Expand All @@ -310,7 +327,8 @@ class SelectionInteractionViewModel private constructor(
writtenTranslationContext,
translationController,
resourceHandler,
userAnswerState
userAnswerState,
consoleLogger
)
}
}
Expand All @@ -320,7 +338,10 @@ class SelectionInteractionViewModel private constructor(
choiceSubtitledHtmls: List<SubtitledHtml>,
hasConversationView: Boolean,
selectionInteractionViewModel: SelectionInteractionViewModel,
enabledItemsList: List<ObservableBoolean>
enabledItemsList: List<ObservableBoolean>,
writtenTranslationContext: WrittenTranslationContext,
translationController: TranslationController,
customTagHandlers: Map<String, CustomHtmlContentHandler.CustomTagHandler>
): ObservableArrayList<SelectionInteractionContentViewModel> {
val observableList = ObservableArrayList<SelectionInteractionContentViewModel>()
observableList += choiceSubtitledHtmls.mapIndexed { index, subtitledHtml ->
Expand All @@ -329,7 +350,10 @@ class SelectionInteractionViewModel private constructor(
hasConversationView = hasConversationView,
itemIndex = index,
selectionInteractionViewModel = selectionInteractionViewModel,
isEnabled = enabledItemsList[index]
isEnabled = enabledItemsList[index],
customTagHandlers,
writtenTranslationContext,
translationController
)
}
return observableList
Expand Down
Loading

0 comments on commit 058259d

Please sign in to comment.