Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio Content highlight [DO NOT MERGE] #491

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.oppia.app.fragment.InjectableFragment
import org.oppia.app.player.state.listener.AudioContentIdListener
import javax.inject.Inject

private const val KEY_EXPLORATION_ID = "EXPLORATION_ID"
Expand Down Expand Up @@ -75,4 +76,8 @@ class AudioFragment : InjectableFragment(), LanguageInterface {
override fun onLanguageSelected(currentLanguageCode: String) {
audioFragmentPresenter.languageSelected(currentLanguageCode)
}

fun setContentIdListener(audioContentIdListener: AudioContentIdListener) {
audioFragmentPresenter.setContentIdListener(audioContentIdListener)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.oppia.app.fragment.FragmentScope
import org.oppia.app.model.Exploration
import org.oppia.app.model.State
import org.oppia.app.model.VoiceoverMapping
import org.oppia.app.player.state.listener.AudioContentIdListener
import org.oppia.app.viewmodel.ViewModelProvider
import org.oppia.domain.exploration.ExplorationDataController
import org.oppia.util.data.AsyncResult
Expand All @@ -40,9 +41,15 @@ class AudioFragmentPresenter @Inject constructor(
private var languages = listOf<String>()

/** Sets up SeekBar listener, ViewModel, and gets VoiceoverMappings or restores saved state */
fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, explorationId: String, stateId: String): View? {
fun handleCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
explorationId: String,
stateId: String
): View? {
val binding = AudioFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
binding.sbAudioProgress.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {
binding.sbAudioProgress.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) {
userProgress = progress
Expand Down Expand Up @@ -131,11 +138,13 @@ class AudioFragmentPresenter @Inject constructor(
processExplorationLiveData(explorationResultLiveData).observe(fragment, Observer {
val state = it.statesMap[stateId] ?: State.getDefaultInstance()
val contentId = state.content.contentId
val voiceoverMapping = (state.recordedVoiceoversMap[contentId] ?: VoiceoverMapping.getDefaultInstance()).voiceoverMappingMap
val voiceoverMapping =
(state.recordedVoiceoversMap[contentId] ?: VoiceoverMapping.getDefaultInstance()).voiceoverMappingMap
languages = voiceoverMapping.keys.toList()
selectedLanguageCode = languages.firstOrNull() ?: ""
val viewModel = getAudioViewModel()
viewModel.setVoiceoverMappings(voiceoverMapping)
viewModel.setContentId(contentId)
viewModel.setAudioLanguageCode(selectedLanguageCode)
})
}
Expand All @@ -154,5 +163,9 @@ class AudioFragmentPresenter @Inject constructor(
}
return explorationResult.getOrDefault(Exploration.getDefaultInstance())
}

fun setContentIdListener(audioContentIdListener: AudioContentIdListener) {
getAudioViewModel().setContentIdListener(audioContentIdListener)
}
}

20 changes: 19 additions & 1 deletion app/src/main/java/org/oppia/app/player/audio/AudioViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.model.Voiceover
import org.oppia.app.player.state.listener.AudioContentIdListener
import org.oppia.domain.audio.AudioPlayerController
import org.oppia.domain.audio.AudioPlayerController.PlayProgress
import org.oppia.domain.audio.AudioPlayerController.PlayStatus
Expand All @@ -20,9 +21,12 @@ class AudioViewModel @Inject constructor(
@DefaultResource private val gcsResource: String
) : ViewModel() {

private lateinit var contentId: String
private lateinit var explorationId: String
private var voiceoverMap = mapOf<String, Voiceover>()

private var audioContentIdListener: AudioContentIdListener? = null

/** Mirrors PlayStatus in AudioPlayerController except adds LOADING state */
enum class UiAudioPlayStatus {
LOADING,
Expand All @@ -44,14 +48,22 @@ class AudioViewModel @Inject constructor(
processPlayStatusLiveData()
}

fun setVoiceoverMappings(map : Map<String, Voiceover>) {
fun setVoiceoverMappings(map: Map<String, Voiceover>) {
voiceoverMap = map
}

fun setContentId(id: String) {
contentId = id
}

fun setExplorationId(id: String) {
explorationId = id
}

fun setContentIdListener(audioContentIdListener: AudioContentIdListener) {
this.audioContentIdListener = audioContentIdListener
}

/** Sets language code for data binding and changes data source to correct audio */
fun setAudioLanguageCode(languageCode: String) {
currentLanguageCode.set(languageCode)
Expand All @@ -62,8 +74,14 @@ class AudioViewModel @Inject constructor(
fun togglePlayPause(type: UiAudioPlayStatus?) {
if (type == UiAudioPlayStatus.PLAYING) {
audioPlayerController.pause()
if (audioContentIdListener != null) {
audioContentIdListener!!.contentIdForCurrentAudio(contentId, isPlaying = false)
}
} else {
audioPlayerController.play()
if (audioContentIdListener != null) {
audioContentIdListener!!.contentIdForCurrentAudio(contentId, isPlaying = true)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import org.oppia.app.fragment.InjectableFragment
import org.oppia.app.model.UserAnswer
import org.oppia.app.player.audio.CellularDataInterface
import org.oppia.app.player.state.answerhandling.InteractionAnswerReceiver
import org.oppia.app.player.state.listener.AudioContentIdListener
import javax.inject.Inject

/** Fragment that represents the current state of an exploration. */
class StateFragment : InjectableFragment(), CellularDataInterface, InteractionAnswerReceiver {
class StateFragment : InjectableFragment(), CellularDataInterface, InteractionAnswerReceiver, AudioContentIdListener {
companion object {
/**
* Creates a new instance of a StateFragment.
Expand Down Expand Up @@ -55,4 +56,8 @@ class StateFragment : InjectableFragment(), CellularDataInterface, InteractionAn
fun handlePlayAudio() = stateFragmentPresenter.handleAudioClick()

fun handleKeyboardAction() = stateFragmentPresenter.handleKeyboardAction()

override fun contentIdForCurrentAudio(contentId: String, isPlaying: Boolean) {
stateFragmentPresenter.handleContentCardHighlighting(contentId, isPlaying)
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package org.oppia.app.player.state

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.os.Handler
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import android.view.animation.AlphaAnimation
import android.view.animation.AnimationSet
import android.view.animation.DecelerateInterpolator
import android.view.animation.TranslateAnimation
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
Expand Down Expand Up @@ -60,6 +55,7 @@ import org.oppia.app.player.state.itemviewmodel.StateNavigationButtonViewModel
import org.oppia.app.player.state.itemviewmodel.StateNavigationButtonViewModel.ContinuationNavigationButtonType
import org.oppia.app.player.state.itemviewmodel.SubmittedAnswerViewModel
import org.oppia.app.player.state.itemviewmodel.TextInputViewModel
import org.oppia.app.player.state.listener.AudioContentIdListener
import org.oppia.app.player.state.listener.PreviousResponsesHeaderClickListener
import org.oppia.app.player.state.listener.StateNavigationButtonListener
import org.oppia.app.recyclerview.BindableAdapter
Expand Down Expand Up @@ -100,6 +96,7 @@ class StateFragmentPresenter @Inject constructor(
private lateinit var binding: StateFragmentBinding
private lateinit var recyclerViewAdapter: RecyclerView.Adapter<*>
private lateinit var viewModel: StateViewModel
private var contentViewModel: ContentViewModel? = null
private val ephemeralStateLiveData: LiveData<AsyncResult<EphemeralState>> by lazy {
explorationProgressController.getCurrentState()
}
Expand Down Expand Up @@ -152,7 +149,11 @@ class StateFragmentPresenter @Inject constructor(
oldBottom: Int
) {
if (bottom < oldBottom) {
binding.stateRecyclerView.postDelayed(Runnable { binding.stateRecyclerView.scrollToPosition(stateRecyclerViewAdapter.getItemCount()-1) }, 100)
binding.stateRecyclerView.postDelayed(Runnable {
binding.stateRecyclerView.scrollToPosition(
stateRecyclerViewAdapter.getItemCount() - 1
)
}, 100)
}
}
})
Expand Down Expand Up @@ -181,6 +182,7 @@ class StateFragmentPresenter @Inject constructor(
htmlParserFactory.create(entityType, explorationId, /* imageCenterAlign= */ true).parseOppiaHtml(
(viewModel as ContentViewModel).htmlContent.toString(), binding.contentTextView
)
binding.viewModel = contentViewModel!!
}
)
.registerViewBinder(
Expand Down Expand Up @@ -319,9 +321,11 @@ class StateFragmentPresenter @Inject constructor(
if (currentYOffset == 0) {
binding.stateRecyclerView.smoothScrollToPosition(0)
}
(getAudioFragment() as AudioFragment).setContentIdListener(fragment as AudioContentIdListener)
} else {
if (getAudioFragment() != null) {
fragment.childFragmentManager.beginTransaction().remove(getAudioFragment()!!).commitNow()
handleContentCardHighlighting(contentViewModel!!.contentId,false)
}
}
}
Expand Down Expand Up @@ -397,8 +401,8 @@ class StateFragmentPresenter @Inject constructor(
answerOutcomeLiveData.observe(fragment, Observer<AnswerOutcome> { result ->
// If the answer was submitted on behalf of the Continue interaction, automatically continue to the next state.
if (result.state.interaction.id == "Continue") {
moveToNextState()
}else if (result.labelledAsCorrectAnswer){
moveToNextState()
} else if (result.labelledAsCorrectAnswer) {
showCongratulationMessageOnCorrectAnswer()
}
})
Expand All @@ -424,7 +428,7 @@ class StateFragmentPresenter @Inject constructor(
Handler().postDelayed({
binding.congratulationTextview.clearAnimation()
binding.congratulationTextview.visibility = View.INVISIBLE
},2000)
}, 2000)
}

/** Helper for subscribeToAnswerOutcome. */
Expand Down Expand Up @@ -452,8 +456,8 @@ class StateFragmentPresenter @Inject constructor(
}

fun handleKeyboardAction() {
hideKeyboard()
handleSubmitAnswer(viewModel.getPendingAnswer())
hideKeyboard()
handleSubmitAnswer(viewModel.getPendingAnswer())
}

override fun onContinueButtonClicked() {
Expand Down Expand Up @@ -492,7 +496,8 @@ class StateFragmentPresenter @Inject constructor(

private fun addContentItem(pendingItemList: MutableList<StateItemViewModel>, ephemeralState: EphemeralState) {
val contentSubtitledHtml: SubtitledHtml = ephemeralState.state.content
pendingItemList += ContentViewModel(contentSubtitledHtml.html)
contentViewModel = ContentViewModel(contentSubtitledHtml.contentId, contentSubtitledHtml.html)
pendingItemList += contentViewModel!!
}

private fun addPreviousAnswers(
Expand Down Expand Up @@ -607,4 +612,10 @@ class StateFragmentPresenter @Inject constructor(
val inputManager: InputMethodManager = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputManager.hideSoftInputFromWindow(fragment.view!!.windowToken, InputMethodManager.SHOW_FORCED)
}

fun handleContentCardHighlighting(contentId: String, playing: Boolean) {
if(contentViewModel!=null && contentViewModel!!.contentId == contentId){
contentViewModel!!.updateIsAudioPlaying(playing)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
package org.oppia.app.player.state.itemviewmodel

import androidx.databinding.ObservableField

/** [ViewModel] for content-card state. */
class ContentViewModel(val htmlContent: CharSequence) : StateItemViewModel(ViewType.CONTENT)
class ContentViewModel(val contentId: String, val htmlContent: CharSequence) :
StateItemViewModel(ViewType.CONTENT){
val isAudioPlaying = ObservableField<Boolean>(false)

fun updateIsAudioPlaying(isPlaying: Boolean){
isAudioPlaying.set(isPlaying)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.oppia.app.player.state.listener

/** Listener when audio is played/paused to highlight the content-card. */
interface AudioContentIdListener {
fun contentIdForCurrentAudio(contentId: String, isPlaying: Boolean)
}
12 changes: 12 additions & 0 deletions app/src/main/res/drawable/content_yellow_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="2dp"
android:color="@color/oppiaContentHighlighting"/>
<corners
android:radius="4dp"/>
<solid
android:color="@color/oppiaSolidBlue"/>
</shape>
8 changes: 6 additions & 2 deletions app/src/main/res/layout/content_item.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

<import type="android.view.View" />

<variable
name="viewModel"
type="org.oppia.app.player.state.itemviewmodel.ContentViewModel" />

<variable
name="htmlContent"
type="CharSequence" />
Expand All @@ -14,9 +18,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="@dimen/divider_margin_top"
android:layout_marginBottom="@dimen/divider_margin_bottom"
android:layout_marginEnd="28dp"
android:background="@drawable/content_blue_background">
android:layout_marginBottom="@dimen/divider_margin_bottom"
android:background="@{viewModel.isAudioPlaying.get()? @drawable/content_yellow_background : @drawable/content_blue_background}">

<TextView
android:id="@+id/content_text_view"
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/layout/state_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
android:layout_height="match_parent"
android:clipToPadding="false"
android:divider="@android:color/transparent"
android:elevation="4dp"
android:overScrollMode="never"
android:paddingTop="@{viewModel.isAudioBarVisible().get()? @dimen/padding_72dp : @dimen/padding_24dp}"
android:scrollbars="none"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<color name="oppiaDisabledButtonBackground">#61999999</color>
<color name="oppiaGreyBorder">#DDDDDD</color>
<color name="oppiaDashedDivider">#80707070</color>
<color name="oppiaContentHighlighting">#F2D140</color>
<!-- BASIC COLORS -->
<color name="white">#FFFFFF</color>
<color name="white_80">#CCFFFFFF</color>
Expand Down