Skip to content

Commit 8e64251

Browse files
feat: video navigation
1 parent 71cc7de commit 8e64251

File tree

6 files changed

+232
-13
lines changed

6 files changed

+232
-13
lines changed

app/src/main/java/org/openedx/app/di/ScreenModule.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.openedx.auth.presentation.signin.SignInViewModel
1313
import org.openedx.auth.presentation.signup.SignUpViewModel
1414
import org.openedx.core.Validator
1515
import org.openedx.core.domain.interactor.CalendarInteractor
16+
import org.openedx.core.presentation.course.CourseViewMode
1617
import org.openedx.core.presentation.dialog.selectorbottomsheet.SelectDialogViewModel
1718
import org.openedx.core.presentation.settings.video.VideoQualityViewModel
1819
import org.openedx.core.repository.CalendarRepository
@@ -340,10 +341,13 @@ val screenModule = module {
340341
get(),
341342
)
342343
}
343-
viewModel { (courseId: String, unitId: String) ->
344+
viewModel { (courseId: String, unitId: String, mode: CourseViewMode) ->
344345
CourseUnitContainerViewModel(
345346
courseId,
346347
unitId,
348+
mode,
349+
get(),
350+
get(),
347351
get(),
348352
get(),
349353
get(),

course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,9 +700,11 @@ fun CourseVideoItem(
700700
titleStyle: TextStyle = MaterialTheme.appTypography.bodySmall,
701701
contentModifier: Modifier = Modifier.padding(8.dp),
702702
progressModifier: Modifier = Modifier.height(4.dp),
703+
playButtonSize: Dp = 32.dp
703704
) {
704705
Box(
705706
modifier = modifier
707+
.clip(MaterialTheme.appShapes.videoPreviewShape)
706708
.let {
707709
if (videoBlock.isCompleted()) {
708710
it.border(
@@ -748,7 +750,7 @@ fun CourseVideoItem(
748750
) {
749751
Image(
750752
modifier = Modifier
751-
.size(32.dp)
753+
.size(playButtonSize)
752754
.align(Alignment.Center),
753755
painter = painterResource(id = R.drawable.course_video_play_button),
754756
contentDescription = null,

course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerFragment.kt

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@ import android.view.LayoutInflater
66
import android.view.View
77
import android.view.ViewGroup
88
import androidx.activity.OnBackPressedCallback
9+
import androidx.compose.foundation.layout.Arrangement
10+
import androidx.compose.foundation.layout.Column
11+
import androidx.compose.foundation.layout.PaddingValues
12+
import androidx.compose.foundation.layout.Spacer
13+
import androidx.compose.foundation.layout.height
914
import androidx.compose.foundation.layout.width
15+
import androidx.compose.foundation.lazy.LazyRow
16+
import androidx.compose.foundation.lazy.items
17+
import androidx.compose.material.Divider
1018
import androidx.compose.material.MaterialTheme
1119
import androidx.compose.runtime.Composable
1220
import androidx.compose.runtime.collectAsState
@@ -31,16 +39,19 @@ import org.koin.android.ext.android.inject
3139
import org.koin.androidx.viewmodel.ext.android.viewModel
3240
import org.koin.core.parameter.parametersOf
3341
import org.openedx.core.BlockType
42+
import org.openedx.core.domain.model.Block
3443
import org.openedx.core.presentation.course.CourseViewMode
3544
import org.openedx.core.presentation.global.InsetHolder
3645
import org.openedx.core.ui.theme.OpenEdXTheme
3746
import org.openedx.core.ui.theme.appColors
47+
import org.openedx.core.ui.theme.appTypography
3848
import org.openedx.course.R
3949
import org.openedx.course.databinding.FragmentCourseUnitContainerBinding
4050
import org.openedx.course.presentation.ChapterEndFragmentDialog
4151
import org.openedx.course.presentation.CourseRouter
4252
import org.openedx.course.presentation.DialogListener
4353
import org.openedx.course.presentation.ui.CourseUnitToolbar
54+
import org.openedx.course.presentation.ui.CourseVideoItem
4455
import org.openedx.course.presentation.ui.HorizontalPageIndicator
4556
import org.openedx.course.presentation.ui.NavigationUnitsButtons
4657
import org.openedx.course.presentation.ui.SubSectionUnitsList
@@ -57,7 +68,8 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
5768
private val viewModel by viewModel<CourseUnitContainerViewModel> {
5869
parametersOf(
5970
requireArguments().getString(ARG_COURSE_ID, ""),
60-
requireArguments().getString(UNIT_ID, "")
71+
requireArguments().getString(UNIT_ID, ""),
72+
requireArguments().serializable(ARG_MODE)
6173
)
6274
}
6375

@@ -96,7 +108,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
96108
fm = requireActivity().supportFragmentManager,
97109
courseId = viewModel.courseId,
98110
unitId = it.id,
99-
mode = requireArguments().serializable(ARG_MODE)!!
111+
mode = viewModel.mode
100112
)
101113
}
102114
}
@@ -133,7 +145,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
133145
super.onCreate(savedInstanceState)
134146
lifecycle.addObserver(viewModel)
135147
componentId = requireArguments().getString(ARG_COMPONENT_ID, "")
136-
viewModel.loadBlocks(requireArguments().serializable(ARG_MODE)!!, componentId)
148+
viewModel.loadBlocks(viewModel.mode, componentId)
137149
viewModel.courseUnitContainerShowedEvent()
138150
}
139151

@@ -157,6 +169,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
157169
setupProgressIndicators()
158170
setupBackButton()
159171
setupSubSectionUnits()
172+
setupVideoList()
160173
checkUnitsListShown()
161174
setupChapterEndDialogListener()
162175
}
@@ -210,7 +223,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
210223
blocks = descendantsBlocks,
211224
selectedPage = index,
212225
completedAndSelectedColor =
213-
MaterialTheme.appColors.componentHorizontalProgressCompletedAndSelected,
226+
MaterialTheme.appColors.componentHorizontalProgressCompletedAndSelected,
214227
completedColor = MaterialTheme.appColors.componentHorizontalProgressCompleted,
215228
selectedColor = MaterialTheme.appColors.componentHorizontalProgressSelected,
216229
defaultColor = MaterialTheme.appColors.componentHorizontalProgressDefault
@@ -294,7 +307,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
294307
fm = requireActivity().supportFragmentManager,
295308
courseId = viewModel.courseId,
296309
unitId = unit.id,
297-
mode = requireArguments().serializable(ARG_MODE)!!
310+
mode = viewModel.mode
298311
)
299312
} else {
300313
handleUnitsClick()
@@ -308,6 +321,37 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
308321
}
309322
}
310323

324+
private fun setupVideoList() {
325+
binding.videoList?.setContent {
326+
OpenEdXTheme {
327+
Column {
328+
VideoList(
329+
onVideoClick = { block ->
330+
val currentBlock = viewModel.currentBlock.value
331+
if (currentBlock?.id != block.id) {
332+
viewModel.setSelectedVideoBlock(block)
333+
updateViewPagerAdapter()
334+
val blockIndex =
335+
viewModel.getUnitBlocks().indexOfFirst { it.id == block.id }
336+
if (blockIndex != -1) {
337+
binding.viewPager.currentItem = blockIndex
338+
}
339+
}
340+
}
341+
)
342+
Spacer(modifier = Modifier.height(8.dp))
343+
Divider()
344+
Spacer(modifier = Modifier.height(8.dp))
345+
}
346+
}
347+
}
348+
}
349+
350+
private fun updateViewPagerAdapter() {
351+
adapter = CourseUnitContainerAdapter(this, viewModel.getUnitBlocks(), viewModel)
352+
binding.viewPager.adapter = adapter
353+
}
354+
311355
private fun checkUnitsListShown() {
312356
if (viewModel.unitsListShowed.value == true) {
313357
handleUnitsClick()
@@ -475,6 +519,42 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
475519
}
476520
}
477521

522+
@Composable
523+
private fun VideoList(
524+
onVideoClick: (Block) -> Unit
525+
) {
526+
val videoBlocks by viewModel.videoList.collectAsState()
527+
val videoPreview by viewModel.videoPreview.collectAsState()
528+
val videoProgress by viewModel.videoProgress.collectAsState()
529+
val currentBlock by viewModel.currentBlock.collectAsState()
530+
531+
if (videoBlocks.isNotEmpty()) {
532+
LazyRow(
533+
horizontalArrangement = Arrangement.spacedBy(8.dp),
534+
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
535+
) {
536+
items(videoBlocks) { block ->
537+
val playButtonSize = if (block.id == currentBlock?.id) {
538+
0.dp
539+
} else {
540+
14.dp
541+
}
542+
CourseVideoItem(
543+
modifier = Modifier
544+
.width(112.dp)
545+
.height(63.dp),
546+
videoBlock = block,
547+
preview = videoPreview[block.id],
548+
progress = videoProgress[block.id] ?: 0f,
549+
onClick = { onVideoClick(block) },
550+
style = MaterialTheme.appTypography.labelSmall,
551+
playButtonSize = playButtonSize,
552+
)
553+
}
554+
}
555+
}
556+
}
557+
478558
companion object {
479559

480560
private const val ARG_COURSE_ID = "courseId"

0 commit comments

Comments
 (0)