Skip to content

Commit

Permalink
merge latest changes from upstream and resolve conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
adhiamboperes committed Feb 27, 2024
2 parents 5a3ebc8 + 6c07b96 commit 971892b
Show file tree
Hide file tree
Showing 39 changed files with 1,086 additions and 198 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import androidx.lifecycle.ViewModel
import org.oppia.android.R
import org.oppia.android.app.fragment.FragmentScope
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.app.utility.getLastUpdateTime
import org.oppia.android.app.utility.getVersionName
import org.oppia.android.app.viewmodel.ObservableViewModel
import org.oppia.android.util.extensions.getLastUpdateTime
import org.oppia.android.util.extensions.getVersionName
import javax.inject.Inject

/** [ViewModel] for [AppVersionFragment]. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
package org.oppia.android.app.notice

import org.oppia.android.app.splash.DeprecationNoticeActionType
import org.oppia.android.app.model.DeprecationNoticeType

/** Listener for when an option on any deprecation dialog is clicked. */
interface DeprecationNoticeActionListener {
/** Called when a dialog button is clicked. */
fun onActionButtonClicked(noticeType: DeprecationNoticeActionType)
fun onActionButtonClicked(noticeActionResponse: DeprecationNoticeActionResponse)
}

/** Sealed data class for the response to a deprecation notice action. */
sealed class DeprecationNoticeActionResponse {
/** Action for when the user presses the 'Close' button on a deprecation dialog. */
object Close : DeprecationNoticeActionResponse()

/** Action for when the user presses the 'Dismiss' button on a deprecation dialog. */
data class Dismiss(
val deprecationNoticeType: DeprecationNoticeType,
val deprecatedVersion: Int,
) : DeprecationNoticeActionResponse()

/** Action for when the user presses the 'Update' button on a deprecation dialog. */
object Update : DeprecationNoticeActionResponse()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import android.app.Dialog
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import org.oppia.android.R
import org.oppia.android.app.splash.DeprecationNoticeActionType
import org.oppia.android.app.translation.AppLanguageResourceHandler
import javax.inject.Inject

/** Presenter class responsible for showing an app deprecation dialog to the user. */
class ForcedAppDeprecationNoticeDialogFragmentPresenter @Inject constructor(
private val activity: AppCompatActivity,
private val resourceHandler: AppLanguageResourceHandler
private val resourceHandler: AppLanguageResourceHandler,
) {
private val deprecationNoticeActionListener by lazy {
activity as DeprecationNoticeActionListener
Expand All @@ -31,12 +30,12 @@ class ForcedAppDeprecationNoticeDialogFragmentPresenter @Inject constructor(
)
.setPositiveButton(R.string.forced_app_update_dialog_update_button_text) { _, _ ->
deprecationNoticeActionListener.onActionButtonClicked(
DeprecationNoticeActionType.UPDATE
DeprecationNoticeActionResponse.Update
)
}
.setNegativeButton(R.string.forced_app_update_dialog_close_button_text) { _, _ ->
deprecationNoticeActionListener.onActionButtonClicked(
DeprecationNoticeActionType.CLOSE
DeprecationNoticeActionResponse.Close
)
}
.setCancelable(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import android.app.Dialog
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import org.oppia.android.R
import org.oppia.android.app.splash.DeprecationNoticeActionType
import org.oppia.android.app.model.DeprecationNoticeType
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.util.platformparameter.OptionalAppUpdateVersionCode
import org.oppia.android.util.platformparameter.PlatformParameterValue
import javax.inject.Inject

/** Presenter class responsible for showing an optional update dialog to the user. */
class OptionalAppDeprecationNoticeDialogFragmentPresenter @Inject constructor(
private val activity: AppCompatActivity,
private val resourceHandler: AppLanguageResourceHandler
private val resourceHandler: AppLanguageResourceHandler,
@OptionalAppUpdateVersionCode
private val optionalAppUpdateVersionCode: PlatformParameterValue<Int>,
) {
private val deprecationNoticeActionListener by lazy {
activity as DeprecationNoticeActionListener
Expand All @@ -31,12 +35,15 @@ class OptionalAppDeprecationNoticeDialogFragmentPresenter @Inject constructor(
)
.setPositiveButton(R.string.optional_app_update_dialog_update_button_text) { _, _ ->
deprecationNoticeActionListener.onActionButtonClicked(
DeprecationNoticeActionType.UPDATE
DeprecationNoticeActionResponse.Update
)
}
.setNegativeButton(R.string.optional_app_update_dialog_dismiss_button_text) { _, _ ->
deprecationNoticeActionListener.onActionButtonClicked(
DeprecationNoticeActionType.DISMISS
DeprecationNoticeActionResponse.Dismiss(
deprecationNoticeType = DeprecationNoticeType.APP_DEPRECATION,
deprecatedVersion = optionalAppUpdateVersionCode.value
)
)
}
.setCancelable(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import android.app.Dialog
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import org.oppia.android.R
import org.oppia.android.app.splash.DeprecationNoticeActionType
import org.oppia.android.app.model.DeprecationNoticeType
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.util.platformparameter.LowestSupportedApiLevel
import org.oppia.android.util.platformparameter.PlatformParameterValue
import javax.inject.Inject

/** Presenter class responsible for showing an OS deprecation dialog to the user. */
class OsDeprecationNoticeDialogFragmentPresenter @Inject constructor(
private val activity: AppCompatActivity,
private val resourceHandler: AppLanguageResourceHandler
private val resourceHandler: AppLanguageResourceHandler,
@LowestSupportedApiLevel
private val lowestSupportedApiLevel: PlatformParameterValue<Int>
) {
private val deprecationNoticeActionListener by lazy {
activity as DeprecationNoticeActionListener
Expand All @@ -31,7 +35,10 @@ class OsDeprecationNoticeDialogFragmentPresenter @Inject constructor(
)
.setNegativeButton(R.string.os_deprecation_dialog_dismiss_button_text) { _, _ ->
deprecationNoticeActionListener.onActionButtonClicked(
DeprecationNoticeActionType.DISMISS
DeprecationNoticeActionResponse.Dismiss(
deprecationNoticeType = DeprecationNoticeType.OS_DEPRECATION,
deprecatedVersion = lowestSupportedApiLevel.value
)
)
}
.setCancelable(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package org.oppia.android.app.notice.testing

import android.os.Bundle
import org.oppia.android.app.notice.DeprecationNoticeActionListener
import org.oppia.android.app.notice.DeprecationNoticeActionResponse
import org.oppia.android.app.notice.ForcedAppDeprecationNoticeDialogFragment
import org.oppia.android.app.splash.DeprecationNoticeActionType
import org.oppia.android.app.testing.activity.TestActivity

/** [TestActivity] for setting up a test environment for testing the beta notice dialog. */
Expand All @@ -24,7 +24,7 @@ class ForcedAppDeprecationNoticeDialogFragmentTestActivity :
.showNow(supportFragmentManager, "forced_app_deprecation_dialog")
}

override fun onActionButtonClicked(noticeType: DeprecationNoticeActionType) {
mockCallbackListener.onActionButtonClicked(noticeType)
override fun onActionButtonClicked(noticeActionResponse: DeprecationNoticeActionResponse) {
mockCallbackListener.onActionButtonClicked(noticeActionResponse)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package org.oppia.android.app.notice.testing

import android.os.Bundle
import org.oppia.android.app.notice.DeprecationNoticeActionListener
import org.oppia.android.app.notice.DeprecationNoticeActionResponse
import org.oppia.android.app.notice.OptionalAppDeprecationNoticeDialogFragment
import org.oppia.android.app.splash.DeprecationNoticeActionType
import org.oppia.android.app.testing.activity.TestActivity

/** [TestActivity] for setting up a test environment for testing the beta notice dialog. */
Expand All @@ -24,7 +24,7 @@ class OptionalAppDeprecationNoticeDialogFragmentTestActivity :
.showNow(supportFragmentManager, "optional_app_deprecation_dialog")
}

override fun onActionButtonClicked(noticeType: DeprecationNoticeActionType) {
mockCallbackListener.onActionButtonClicked(noticeType)
override fun onActionButtonClicked(noticeActionResponse: DeprecationNoticeActionResponse) {
mockCallbackListener.onActionButtonClicked(noticeActionResponse)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package org.oppia.android.app.notice.testing

import android.os.Bundle
import org.oppia.android.app.notice.DeprecationNoticeActionListener
import org.oppia.android.app.notice.DeprecationNoticeActionResponse
import org.oppia.android.app.notice.OsDeprecationNoticeDialogFragment
import org.oppia.android.app.splash.DeprecationNoticeActionType
import org.oppia.android.app.testing.activity.TestActivity

/** [TestActivity] for setting up a test environment for testing the beta notice dialog. */
Expand All @@ -24,7 +24,7 @@ class OsDeprecationNoticeDialogFragmentTestActivity :
.showNow(supportFragmentManager, "os_deprecation_dialog")
}

override fun onActionButtonClicked(noticeType: DeprecationNoticeActionType) {
mockCallbackListener.onActionButtonClicked(noticeType)
override fun onActionButtonClicked(noticeActionResponse: DeprecationNoticeActionResponse) {
mockCallbackListener.onActionButtonClicked(noticeActionResponse)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ class ExplorationActivityPresenter @Inject constructor(
activity.startActivity(
TopicActivity.createTopicActivityIntent(context, profileId.internalId, topicId)
)
activity.finish()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.oppia.android.app.player.state.itemviewmodel

import androidx.annotation.StringRes
import androidx.databinding.Observable
import androidx.databinding.ObservableField
import org.oppia.android.R
Expand All @@ -9,6 +10,7 @@ import org.oppia.android.app.model.Interaction
import org.oppia.android.app.model.InteractionObject
import org.oppia.android.app.model.UserAnswer
import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver
Expand All @@ -31,6 +33,9 @@ class ImageRegionSelectionInteractionViewModel private constructor(
) : StateItemViewModel(ViewType.IMAGE_REGION_SELECTION_INTERACTION),
InteractionAnswerHandler,
OnClickableAreaClickedListener {
private var pendingAnswerError: String? = null
var errorMessage = ObservableField<String>("")
private var isDefaultRegionClicked = false
var answerText: CharSequence = ""
val selectableRegions: List<ImageWithRegions.LabeledRegion> by lazy {
val schemaObject = interaction.customizationArgsMap["imageAndRegions"]
Expand All @@ -49,25 +54,58 @@ class ImageRegionSelectionInteractionViewModel private constructor(
object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable, propertyId: Int) {
errorOrAvailabilityCheckReceiver.onPendingAnswerErrorOrAvailabilityCheck(
pendingAnswerError = null,
inputAnswerAvailable = answerText.isNotEmpty()
pendingAnswerError = pendingAnswerError,
inputAnswerAvailable = true // Allow blank answer submission.
)
}
}
isAnswerAvailable.addOnPropertyChangedCallback(callback)
errorMessage.addOnPropertyChangedCallback(callback)

// Initializing with default values so that submit button is enabled by default.
errorOrAvailabilityCheckReceiver.onPendingAnswerErrorOrAvailabilityCheck(
pendingAnswerError = null,
inputAnswerAvailable = true
)
}

override fun onClickableAreaTouched(region: RegionClickedEvent) {

when (region) {
is DefaultRegionClickedEvent -> {
answerText = ""
isAnswerAvailable.set(false)
isDefaultRegionClicked = true
}
is NamedRegionClickedEvent -> {
answerText = region.regionLabel
isAnswerAvailable.set(true)
}
}
checkPendingAnswerError(AnswerErrorCategory.REAL_TIME)
}

/** It checks the pending error for the current image region input, and correspondingly updates the error string based on the specified error category. */
override fun checkPendingAnswerError(category: AnswerErrorCategory): String? {
when (category) {
AnswerErrorCategory.REAL_TIME -> {
pendingAnswerError = null
}

AnswerErrorCategory.SUBMIT_TIME -> {
if (answerText.isNotEmpty() || isDefaultRegionClicked) {
pendingAnswerError = null
} else {
pendingAnswerError =
ImageRegionParsingUiError.createFromParsingError(
getSubmitTimeError(answerText.toString())
).getErrorMessageFromStringRes(resourceHandler)
}
}
}

errorMessage.set(pendingAnswerError)
return pendingAnswerError
}

override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply {
Expand All @@ -91,10 +129,60 @@ class ImageRegionSelectionInteractionViewModel private constructor(
.build()
}

/**
* Returns [ImageRegionParsingError.EMPTY_INPUT] if input is blank, or
* [TextParsingError.VALID] if input is not empty.
*/
fun getSubmitTimeError(text: String): ImageRegionParsingError {
if (text.isNullOrBlank()) {
return ImageRegionParsingError.EMPTY_INPUT
}
return ImageRegionParsingError.VALID
}

/** Represents errors that can occur when parsing region name. */
enum class ImageRegionParsingError {

/** Indicates that the considered string is a valid. */
VALID,

/** Indicates that the input text was empty. */
EMPTY_INPUT
}

enum class ImageRegionParsingUiError(@StringRes private var error: Int?) {
/** Corresponds to [ImageRegionParsingError.VALID]. */
VALID(error = null),

/** Corresponds to [ImageRegionParsingError.EMPTY_INPUT]. */
EMPTY_INPUT(error = R.string.image_error_empty_input);

/**
* Returns the string corresponding to this error's string resources, or null if there is none.
*/
fun getErrorMessageFromStringRes(resourceHandler: AppLanguageResourceHandler): String? =
error?.let(resourceHandler::getStringInLocale)

companion object {
/**
* Returns the [ImageRegionParsingUiError] corresponding to the specified [ImageRegionParsingError].
*/
fun createFromParsingError(parsingError: ImageRegionParsingError): ImageRegionParsingUiError {
return when (parsingError) {

ImageRegionParsingError.VALID -> VALID

ImageRegionParsingError.EMPTY_INPUT -> EMPTY_INPUT
}
}
}
}

/** Implementation of [StateItemViewModel.InteractionItemFactory] for this view model. */
class FactoryImpl @Inject constructor(
private val resourceHandler: AppLanguageResourceHandler
) : InteractionItemFactory {

override fun create(
entityId: String,
hasConversationView: Boolean,
Expand Down
Loading

0 comments on commit 971892b

Please sign in to comment.