Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f7cfb43
feat: course home pager and pager indicator with navigation
PavloNetrebchuk Jul 24, 2025
5ac6548
feat: course completion pager tab
PavloNetrebchuk Sep 2, 2025
7d6610f
feat: move HomeNavigationRow to bottom bar
PavloNetrebchuk Sep 3, 2025
8625632
feat: course home pages videos card
PavloNetrebchuk Sep 3, 2025
d1ed4bf
feat: course home pages assignment card
PavloNetrebchuk Sep 3, 2025
87da809
feat: course home pages grades card
PavloNetrebchuk Sep 4, 2025
2ce9ceb
feat: added empty states
PavloNetrebchuk Sep 4, 2025
11657ff
feat: CaughtUpMessage
PavloNetrebchuk Sep 4, 2025
c9f3cbe
feat: A11y
PavloNetrebchuk Sep 4, 2025
36e49ac
feat: detekt fixes
PavloNetrebchuk Sep 4, 2025
05fc280
feat: changes according demo feedback
PavloNetrebchuk Sep 5, 2025
42eda19
feat: course home analytic
PavloNetrebchuk Sep 5, 2025
a408106
feat: CourseHomeViewModelTest
PavloNetrebchuk Sep 5, 2025
14b9923
fix: detekt fix and changes according PR review
PavloNetrebchuk Sep 8, 2025
5e1b11f
feat: performance improvements
PavloNetrebchuk Sep 16, 2025
71cc7de
feat: changes according PR feedback
PavloNetrebchuk Sep 22, 2025
762ffe2
feat: changes according PR feedback
PavloNetrebchuk Oct 7, 2025
86351b3
feat: video navigation
PavloNetrebchuk Sep 8, 2025
f31162d
feat: minor ui fixes
PavloNetrebchuk Sep 10, 2025
ac10f8c
feat: next prev buttons logic update for video view mode
PavloNetrebchuk Oct 6, 2025
abbb92f
Merge branch 'develop' into feat/video_navigation
PavloNetrebchuk Oct 7, 2025
165d9bd
fix: db error fix
PavloNetrebchuk Oct 8, 2025
5f23bf3
fix: button arrow fix
PavloNetrebchuk Oct 8, 2025
795c43e
fix: changes according designer review
PavloNetrebchuk Oct 22, 2025
a0bf106
fix: obfuscation fix
PavloNetrebchuk Oct 22, 2025
63fd961
fix: changes according code review feedback
PavloNetrebchuk Oct 22, 2025
02ab146
fix: update androidyoutubeplayer to fix error 15
IvanStepanok Oct 23, 2025
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
1,152 changes: 1,152 additions & 0 deletions app/schemas/org.openedx.app.room.AppDatabase/5.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion app/src/main/java/org/openedx/app/di/ScreenModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.openedx.course.presentation.outline.CourseContentAllViewModel
import org.openedx.course.presentation.progress.CourseProgressViewModel
import org.openedx.course.presentation.section.CourseSectionViewModel
import org.openedx.course.presentation.unit.container.CourseUnitContainerViewModel
import org.openedx.course.presentation.unit.container.CourseViewMode
import org.openedx.course.presentation.unit.html.HtmlUnitViewModel
import org.openedx.course.presentation.unit.video.BaseVideoViewModel
import org.openedx.course.presentation.unit.video.EncodedVideoUnitViewModel
Expand Down Expand Up @@ -340,10 +341,12 @@ val screenModule = module {
get(),
)
}
viewModel { (courseId: String, unitId: String) ->
viewModel { (courseId: String, unitId: String, mode: CourseViewMode) ->
CourseUnitContainerViewModel(
courseId,
unitId,
mode,
get(),
get(),
get(),
get(),
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/java/org/openedx/app/room/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import org.openedx.discovery.data.converter.DiscoveryConverter
import org.openedx.discovery.data.model.room.CourseEntity
import org.openedx.discovery.data.storage.DiscoveryDao

const val DATABASE_VERSION = 4
const val DATABASE_VERSION = 5
const val DATABASE_NAME = "OpenEdX_db"

@Suppress("MagicNumber")
Expand All @@ -44,7 +44,8 @@ const val DATABASE_NAME = "OpenEdX_db"
autoMigrations = [
AutoMigration(1, 2),
AutoMigration(2, 3),
AutoMigration(3, DATABASE_VERSION),
AutoMigration(3, 4),
AutoMigration(4, DATABASE_VERSION),
],
version = DATABASE_VERSION
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ data class CourseInfoOverviewDb(
val number: String,
@ColumnInfo("org")
val org: String,
@Embedded
@ColumnInfo("start")
val start: Date?,
@ColumnInfo("startDisplay")
val startDisplay: String,
@ColumnInfo("startType")
val startType: String,
@Embedded
@ColumnInfo("end")
val end: Date?,
@ColumnInfo("isSelfPaced")
val isSelfPaced: Boolean,
Expand Down
16 changes: 8 additions & 8 deletions core/src/openedx/org/openedx/core/ui/theme/Colors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@ val light_onSurface = Color.Black
val light_onError = Color.White
val light_onWarning = Color.White
val light_onInfo = Color.White
val light_info_variant = Color(0xFF3C68FF)
val light_info_variant = light_primary
val light_text_primary = Color(0xFF212121)
val light_text_primary_variant = Color(0xFF3D4964)
val light_text_primary_light = light_text_primary
val light_text_secondary = Color(0xFFB3B3B3)
val light_text_dark = Color(0xFF19212F)
val light_text_accent = Color(0xFF3C68FF)
val light_text_accent = light_primary
val light_text_warning = Color(0xFF19212F)
val light_text_field_background = Color(0xFFF7F7F8)
val light_text_field_background_variant = Color.White
val light_text_field_border = Color(0xFF97A5BB)
val light_text_field_text = Color(0xFF3D4964)
val light_text_field_hint = Color(0xFF97A5BB)
val light_text_hyper_link = Color(0xFF3C68FF)
val light_text_hyper_link = light_primary

val light_primary_button_background = Color(0xFF3C68FF)
val light_primary_button_background = light_primary
val light_primary_button_border = Color(0xFF97A5BB)
val light_primary_button_text = Color.White
val light_primary_button_bordered_text = Color(0xFF3C68FF)
val light_primary_button_bordered_text = light_primary

val light_secondary_button_background = light_primary_button_background
val light_secondary_button_text = light_primary_button_text
Expand Down Expand Up @@ -102,12 +102,12 @@ val dark_text_field_background_variant = Color(0xFF273346)
val dark_text_field_border = Color(0xFF4E5A70)
val dark_text_field_text = Color.White
val dark_text_field_hint = Color(0xFF79889F)
val dark_text_hyper_link = Color(0xFF5478F9)
val dark_text_hyper_link = dark_primary

val dark_primary_button_background = Color(0xFF5478F9)
val dark_primary_button_background = dark_primary
val dark_primary_button_text = Color.White
val dark_primary_button_border = Color(0xFF4E5A70)
val dark_primary_button_bordered_text = Color(0xFF5478F9)
val dark_primary_button_bordered_text = dark_primary

val dark_secondary_button_background = dark_primary_button_background
val dark_secondary_button_text = dark_primary_button_text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,20 @@ import org.openedx.core.data.model.room.GradingPolicyDb
import org.openedx.core.data.model.room.SectionScoreDb
import org.openedx.core.data.model.room.discovery.CourseDateBlockDb
import org.openedx.foundation.extension.genericType
import java.util.Date

class CourseConverter {

@TypeConverter
fun fromDate(value: Date?): Long? {
return value?.time
}

@TypeConverter
fun toDate(value: Long?): Date? {
return value?.let { Date(it) }
}

@TypeConverter
fun fromListOfString(value: List<String>): String {
val json = Gson().toJson(value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.openedx.course.presentation.home

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
Expand All @@ -10,7 +9,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.MaterialTheme
Expand All @@ -26,9 +24,7 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import org.openedx.core.domain.model.Block
import org.openedx.core.extension.getUnitChapter
import org.openedx.core.ui.theme.appColors
import org.openedx.core.ui.theme.appShapes
import org.openedx.core.ui.theme.appTypography
import org.openedx.course.R
import org.openedx.course.presentation.contenttab.CourseContentVideoEmptyState
Expand Down Expand Up @@ -131,42 +127,21 @@ fun VideosHomePagerCardContent(
Spacer(modifier = Modifier.height(8.dp))

// Video card using CourseVideoItem
Card(
CourseVideoItem(
modifier = Modifier
.fillMaxWidth(),
backgroundColor = MaterialTheme.appColors.cardViewBackground,
shape = MaterialTheme.appShapes.videoPreviewShape,
elevation = 0.dp,
border = BorderStroke(
width = 1.dp,
color = MaterialTheme.appColors.cardViewBorder
)
) {
Column {
CourseVideoItem(
modifier = Modifier
.fillMaxWidth()
.height(180.dp),
videoBlock = firstIncompleteVideo,
preview = uiState.videoPreview,
progress = videoProgress,
onClick = {
onVideoClick(firstIncompleteVideo)
},
titleStyle = MaterialTheme.appTypography.titleMedium,
contentModifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp),
progressModifier = Modifier.height(8.dp),
)
Text(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
text = uiState.courseStructure.blockData
.getUnitChapter(firstIncompleteVideo.id)?.displayName ?: "",
style = MaterialTheme.appTypography.labelMedium,
color = MaterialTheme.appColors.textPrimary,
)
}
}
.fillMaxWidth()
.height(180.dp),
videoBlock = firstIncompleteVideo,
preview = uiState.videoPreview,
progress = videoProgress,
onClick = {
onVideoClick(firstIncompleteVideo)
},
titleStyle = MaterialTheme.appTypography.titleMedium,
contentModifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp),
progressModifier = Modifier.height(8.dp),
)
} else {
CaughtUpMessage(
message = stringResource(R.string.course_videos_caught_up)
Expand Down
117 changes: 70 additions & 47 deletions course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import androidx.compose.material.Snackbar
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.Close
Expand Down Expand Up @@ -338,6 +339,7 @@ fun NavigationUnitsButtons(
nextButtonText: String,
hasPrevBlock: Boolean,
hasNextBlock: Boolean,
showFinishButton: Boolean = true,
isVerticalNavigation: Boolean,
onPrevClick: () -> Unit,
onNextClick: () -> Unit,
Expand Down Expand Up @@ -375,7 +377,7 @@ fun NavigationUnitsButtons(
colors = ButtonDefaults.outlinedButtonColors(
backgroundColor = MaterialTheme.appColors.background
),
border = BorderStroke(1.dp, MaterialTheme.appColors.primaryButtonBorder),
border = BorderStroke(1.dp, MaterialTheme.appColors.textAccent),
elevation = null,
shape = MaterialTheme.appShapes.navigationButtonShape,
onClick = onPrevClick,
Expand All @@ -384,48 +386,66 @@ fun NavigationUnitsButtons(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
if (!isVerticalNavigation) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null,
tint = MaterialTheme.appColors.textAccent
)
Spacer(Modifier.width(8.dp))
}
Text(
text = stringResource(R.string.course_navigation_prev),
color = MaterialTheme.appColors.primary,
color = MaterialTheme.appColors.textAccent,
style = MaterialTheme.appTypography.labelLarge
)
Spacer(Modifier.width(8.dp))
Icon(
modifier = Modifier.rotate(if (isVerticalNavigation) 0f else -90f),
painter = painterResource(id = coreR.drawable.core_ic_up),
contentDescription = null,
tint = MaterialTheme.appColors.primary
)
if (isVerticalNavigation) {
Spacer(Modifier.width(8.dp))
Icon(
painter = painterResource(id = coreR.drawable.core_ic_up),
contentDescription = null,
tint = MaterialTheme.appColors.textAccent
)
}
}
}
Spacer(Modifier.width(16.dp))
}
Button(
modifier = Modifier
.height(42.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.appColors.primaryButtonBackground
),
elevation = null,
shape = MaterialTheme.appShapes.navigationButtonShape,
onClick = onNextClick
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
if (hasNextBlock || showFinishButton) {
Button(
modifier = Modifier
.height(42.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.appColors.primaryButtonBackground
),
elevation = null,
shape = MaterialTheme.appShapes.navigationButtonShape,
onClick = onNextClick
) {
Text(
text = nextButtonText,
color = MaterialTheme.appColors.primaryButtonText,
style = MaterialTheme.appTypography.labelLarge
)
Spacer(Modifier.width(8.dp))
Icon(
modifier = Modifier.rotate(if (isVerticalNavigation || !hasNextBlock) 0f else -90f),
painter = nextButtonIcon,
contentDescription = null,
tint = MaterialTheme.appColors.primaryButtonText
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = nextButtonText,
color = MaterialTheme.appColors.primaryButtonText,
style = MaterialTheme.appTypography.labelLarge
)
Spacer(Modifier.width(8.dp))
if (isVerticalNavigation || !hasNextBlock) {
Icon(
painter = nextButtonIcon,
contentDescription = null,
tint = MaterialTheme.appColors.primaryButtonText
)
} else {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = null,
tint = MaterialTheme.appColors.primaryButtonText
)
}
}
}
}
}
Expand Down Expand Up @@ -700,20 +720,23 @@ fun CourseVideoItem(
titleStyle: TextStyle = MaterialTheme.appTypography.bodySmall,
contentModifier: Modifier = Modifier.padding(8.dp),
progressModifier: Modifier = Modifier.height(4.dp),
playButtonSize: Dp = 32.dp,
borderColor: Color? = null,
borderWidth: Dp = 3.dp,
) {
val borderColor = borderColor ?: if (videoBlock.isCompleted()) {
MaterialTheme.appColors.successGreen
} else {
Color.Transparent
}
Box(
modifier = modifier
.let {
if (videoBlock.isCompleted()) {
it.border(
width = 3.dp,
color = MaterialTheme.appColors.successGreen,
shape = MaterialTheme.appShapes.videoPreviewShape
)
} else {
it
}
}
.clip(MaterialTheme.appShapes.videoPreviewShape)
.border(
width = borderWidth,
color = borderColor,
shape = MaterialTheme.appShapes.videoPreviewShape
)
.clickable { onClick() }
) {
AsyncImage(
Expand Down Expand Up @@ -748,7 +771,7 @@ fun CourseVideoItem(
) {
Image(
modifier = Modifier
.size(32.dp)
.size(playButtonSize)
.align(Alignment.Center),
painter = painterResource(id = R.drawable.course_video_play_button),
contentDescription = null,
Expand All @@ -761,7 +784,7 @@ fun CourseVideoItem(
style = titleStyle,
modifier = Modifier
.align(Alignment.TopStart),
maxLines = 2,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)

Expand Down
Loading
Loading