Skip to content

Commit 6302530

Browse files
feat: video navigation
1 parent b9117cf commit 6302530

File tree

6 files changed

+233
-13
lines changed

6 files changed

+233
-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: 86 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,15 +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
43+
import org.openedx.core.presentation.course.CourseViewMode
3444
import org.openedx.core.presentation.global.InsetHolder
3545
import org.openedx.core.ui.theme.OpenEdXTheme
3646
import org.openedx.core.ui.theme.appColors
47+
import org.openedx.core.ui.theme.appTypography
3748
import org.openedx.course.R
3849
import org.openedx.course.databinding.FragmentCourseUnitContainerBinding
3950
import org.openedx.course.presentation.ChapterEndFragmentDialog
4051
import org.openedx.course.presentation.CourseRouter
4152
import org.openedx.course.presentation.DialogListener
4253
import org.openedx.course.presentation.ui.CourseUnitToolbar
54+
import org.openedx.course.presentation.ui.CourseVideoItem
4355
import org.openedx.course.presentation.ui.HorizontalPageIndicator
4456
import org.openedx.course.presentation.ui.NavigationUnitsButtons
4557
import org.openedx.course.presentation.ui.SubSectionUnitsList
@@ -56,7 +68,8 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
5668
private val viewModel by viewModel<CourseUnitContainerViewModel> {
5769
parametersOf(
5870
requireArguments().getString(ARG_COURSE_ID, ""),
59-
requireArguments().getString(UNIT_ID, "")
71+
requireArguments().getString(UNIT_ID, ""),
72+
requireArguments().serializable(ARG_MODE)
6073
)
6174
}
6275

@@ -95,7 +108,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
95108
fm = requireActivity().supportFragmentManager,
96109
courseId = viewModel.courseId,
97110
unitId = it.id,
98-
mode = requireArguments().serializable(ARG_MODE)!!
111+
mode = viewModel.mode
99112
)
100113
}
101114
}
@@ -132,7 +145,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
132145
super.onCreate(savedInstanceState)
133146
lifecycle.addObserver(viewModel)
134147
componentId = requireArguments().getString(ARG_COMPONENT_ID, "")
135-
viewModel.loadBlocks(requireArguments().serializable(ARG_MODE)!!, componentId)
148+
viewModel.loadBlocks(viewModel.mode, componentId)
136149
viewModel.courseUnitContainerShowedEvent()
137150
}
138151

@@ -156,6 +169,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
156169
setupProgressIndicators()
157170
setupBackButton()
158171
setupSubSectionUnits()
172+
setupVideoList()
159173
checkUnitsListShown()
160174
setupChapterEndDialogListener()
161175
}
@@ -209,7 +223,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
209223
blocks = descendantsBlocks,
210224
selectedPage = index,
211225
completedAndSelectedColor =
212-
MaterialTheme.appColors.componentHorizontalProgressCompletedAndSelected,
226+
MaterialTheme.appColors.componentHorizontalProgressCompletedAndSelected,
213227
completedColor = MaterialTheme.appColors.componentHorizontalProgressCompleted,
214228
selectedColor = MaterialTheme.appColors.componentHorizontalProgressSelected,
215229
defaultColor = MaterialTheme.appColors.componentHorizontalProgressDefault
@@ -293,7 +307,7 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
293307
fm = requireActivity().supportFragmentManager,
294308
courseId = viewModel.courseId,
295309
unitId = unit.id,
296-
mode = requireArguments().serializable(ARG_MODE)!!
310+
mode = viewModel.mode
297311
)
298312
} else {
299313
handleUnitsClick()
@@ -307,6 +321,37 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
307321
}
308322
}
309323

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+
310355
private fun checkUnitsListShown() {
311356
if (viewModel.unitsListShowed.value == true) {
312357
handleUnitsClick()
@@ -474,6 +519,42 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
474519
}
475520
}
476521

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+
477558
companion object {
478559

479560
private const val ARG_COURSE_ID = "courseId"

0 commit comments

Comments
 (0)