Skip to content

Commit 943381b

Browse files
authored
fix: primary course in landscape (#435)
1 parent 9014e63 commit 943381b

File tree

1 file changed

+181
-91
lines changed

1 file changed

+181
-91
lines changed

dashboard/src/main/java/org/openedx/courses/presentation/DashboardGalleryView.kt

Lines changed: 181 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import androidx.compose.foundation.clickable
66
import androidx.compose.foundation.layout.Arrangement
77
import androidx.compose.foundation.layout.Box
88
import androidx.compose.foundation.layout.Column
9+
import androidx.compose.foundation.layout.IntrinsicSize
910
import androidx.compose.foundation.layout.PaddingValues
1011
import androidx.compose.foundation.layout.Row
1112
import androidx.compose.foundation.layout.Spacer
@@ -52,6 +53,7 @@ import androidx.compose.ui.Modifier
5253
import androidx.compose.ui.graphics.painter.Painter
5354
import androidx.compose.ui.graphics.vector.rememberVectorPainter
5455
import androidx.compose.ui.layout.ContentScale
56+
import androidx.compose.ui.platform.LocalConfiguration
5557
import androidx.compose.ui.platform.LocalContext
5658
import androidx.compose.ui.platform.testTag
5759
import androidx.compose.ui.res.painterResource
@@ -61,6 +63,7 @@ import androidx.compose.ui.text.style.TextAlign
6163
import androidx.compose.ui.text.style.TextOverflow
6264
import androidx.compose.ui.tooling.preview.Devices
6365
import androidx.compose.ui.tooling.preview.Preview
66+
import androidx.compose.ui.unit.Dp
6467
import androidx.compose.ui.unit.dp
6568
import androidx.fragment.app.FragmentManager
6669
import androidx.lifecycle.Lifecycle
@@ -88,6 +91,7 @@ import org.openedx.core.ui.HandleUIMessage
8891
import org.openedx.core.ui.OfflineModeDialog
8992
import org.openedx.core.ui.OpenEdXButton
9093
import org.openedx.core.ui.TextIcon
94+
import org.openedx.core.ui.displayCutoutForLandscape
9195
import org.openedx.core.ui.theme.OpenEdXTheme
9296
import org.openedx.core.ui.theme.appColors
9397
import org.openedx.core.ui.theme.appShapes
@@ -200,6 +204,7 @@ private fun DashboardGalleryView(
200204
Surface(
201205
modifier = Modifier
202206
.fillMaxSize()
207+
.displayCutoutForLandscape()
203208
.padding(paddingValues),
204209
color = MaterialTheme.appColors.background
205210
) {
@@ -528,7 +533,7 @@ private fun PrimaryCourseCard(
528533
resumeBlockId: (enrolledCourse: EnrolledCourse, blockId: String) -> Unit,
529534
openCourse: (EnrolledCourse) -> Unit,
530535
) {
531-
val context = LocalContext.current
536+
val orientation = LocalConfiguration.current.orientation
532537
Card(
533538
modifier = Modifier
534539
.padding(horizontal = 16.dp)
@@ -538,103 +543,184 @@ private fun PrimaryCourseCard(
538543
shape = MaterialTheme.appShapes.courseImageShape,
539544
elevation = 4.dp
540545
) {
541-
Column(
542-
modifier = Modifier
543-
.clickable {
544-
openCourse(primaryCourse)
545-
}
546-
) {
547-
AsyncImage(
548-
model = ImageRequest.Builder(context)
549-
.data(primaryCourse.course.courseImage.toImageLink(apiHostUrl))
550-
.error(CoreR.drawable.core_no_image_course)
551-
.placeholder(CoreR.drawable.core_no_image_course)
552-
.build(),
553-
contentDescription = null,
554-
contentScale = ContentScale.Crop,
555-
modifier = Modifier
556-
.fillMaxWidth()
557-
.height(140.dp)
558-
)
559-
val progress: Float = try {
560-
primaryCourse.progress.assignmentsCompleted.toFloat() /
561-
primaryCourse.progress.totalAssignmentsCount.toFloat()
562-
} catch (_: ArithmeticException) {
563-
0f
564-
}
565-
LinearProgressIndicator(
566-
modifier = Modifier
567-
.fillMaxWidth()
568-
.height(8.dp),
569-
progress = progress,
570-
color = MaterialTheme.appColors.primary,
571-
backgroundColor = MaterialTheme.appColors.divider
572-
)
573-
PrimaryCourseTitle(
574-
modifier = Modifier
575-
.fillMaxWidth()
576-
.padding(horizontal = 12.dp)
577-
.padding(top = 8.dp, bottom = 16.dp),
578-
primaryCourse = primaryCourse
579-
)
580-
val pastAssignments = primaryCourse.courseAssignments?.pastAssignments
581-
if (!pastAssignments.isNullOrEmpty()) {
582-
val nearestAssignment = pastAssignments.maxBy { it.date }
583-
val title = if (pastAssignments.size == 1) nearestAssignment.title else null
584-
Divider()
585-
AssignmentItem(
586-
modifier = Modifier.clickable {
587-
if (pastAssignments.size == 1) {
588-
resumeBlockId(primaryCourse, nearestAssignment.blockId)
589-
} else {
590-
navigateToDates(primaryCourse)
546+
when (orientation) {
547+
Configuration.ORIENTATION_LANDSCAPE -> {
548+
Row(
549+
modifier = Modifier
550+
.clickable {
551+
openCourse(primaryCourse)
591552
}
592-
},
593-
painter = rememberVectorPainter(Icons.Default.Warning),
594-
title = title,
595-
info = pluralStringResource(
596-
R.plurals.dashboard_past_due_assignment,
597-
pastAssignments.size,
598-
pastAssignments.size
553+
.height(IntrinsicSize.Min)
554+
) {
555+
PrimaryCourseCaption(
556+
modifier = Modifier.weight(1f),
557+
primaryCourse = primaryCourse,
558+
apiHostUrl = apiHostUrl,
559+
imageHeight = null,
599560
)
600-
)
561+
PrimaryCourseButtons(
562+
modifier = Modifier.weight(1f),
563+
primaryCourse = primaryCourse,
564+
navigateToDates = navigateToDates,
565+
resumeBlockId = resumeBlockId,
566+
openCourse = openCourse,
567+
adjustHeight = true,
568+
useRelativeDates = useRelativeDates,
569+
)
570+
}
601571
}
602-
val futureAssignments = primaryCourse.courseAssignments?.futureAssignments
603-
if (!futureAssignments.isNullOrEmpty()) {
604-
val nearestAssignment = futureAssignments.minBy { it.date }
605-
val title = if (futureAssignments.size == 1) nearestAssignment.title else null
606-
Divider()
607-
AssignmentItem(
572+
573+
else -> {
574+
Column(
608575
modifier = Modifier.clickable {
609-
if (futureAssignments.size == 1) {
610-
resumeBlockId(primaryCourse, nearestAssignment.blockId)
611-
} else {
612-
navigateToDates(primaryCourse)
613-
}
614-
},
615-
painter = painterResource(id = CoreR.drawable.ic_core_chapter_icon),
616-
title = title,
617-
info = stringResource(
618-
R.string.dashboard_assignment_due,
619-
nearestAssignment.assignmentType ?: "",
620-
stringResource(
621-
id = CoreR.string.core_date_format_assignment_due,
622-
TimeUtils.formatToString(context, nearestAssignment.date, useRelativeDates)
623-
)
576+
openCourse(primaryCourse)
577+
}
578+
) {
579+
PrimaryCourseCaption(
580+
primaryCourse = primaryCourse,
581+
apiHostUrl = apiHostUrl,
624582
)
625-
)
583+
PrimaryCourseButtons(
584+
primaryCourse = primaryCourse,
585+
navigateToDates = navigateToDates,
586+
resumeBlockId = resumeBlockId,
587+
openCourse = openCourse,
588+
useRelativeDates = useRelativeDates,
589+
)
590+
}
626591
}
627-
ResumeButton(
628-
primaryCourse = primaryCourse,
629-
onClick = {
630-
if (primaryCourse.courseStatus == null) {
631-
openCourse(primaryCourse)
592+
}
593+
}
594+
}
595+
596+
@Composable
597+
private fun PrimaryCourseButtons(
598+
modifier: Modifier = Modifier,
599+
primaryCourse: EnrolledCourse,
600+
useRelativeDates: Boolean,
601+
adjustHeight: Boolean = false,
602+
navigateToDates: (EnrolledCourse) -> Unit,
603+
resumeBlockId: (enrolledCourse: EnrolledCourse, blockId: String) -> Unit,
604+
openCourse: (EnrolledCourse) -> Unit,
605+
) {
606+
val context = LocalContext.current
607+
val pastAssignments = primaryCourse.courseAssignments?.pastAssignments
608+
Column(modifier = modifier) {
609+
var titleModifier = Modifier
610+
.fillMaxWidth()
611+
.padding(horizontal = 12.dp)
612+
.padding(top = 8.dp, bottom = 16.dp)
613+
if (adjustHeight) {
614+
titleModifier = titleModifier.weight(1f)
615+
}
616+
PrimaryCourseTitle(
617+
modifier = titleModifier,
618+
primaryCourse = primaryCourse,
619+
)
620+
Divider()
621+
if (!pastAssignments.isNullOrEmpty()) {
622+
val nearestAssignment = pastAssignments.maxBy { it.date }
623+
val title = if (pastAssignments.size == 1) nearestAssignment.title else null
624+
AssignmentItem(
625+
modifier = Modifier.clickable {
626+
if (pastAssignments.size == 1) {
627+
resumeBlockId(primaryCourse, nearestAssignment.blockId)
632628
} else {
633-
resumeBlockId(primaryCourse, primaryCourse.courseStatus?.lastVisitedBlockId ?: "")
629+
navigateToDates(primaryCourse)
634630
}
635-
}
631+
},
632+
painter = rememberVectorPainter(Icons.Default.Warning),
633+
title = title,
634+
info = pluralStringResource(
635+
R.plurals.dashboard_past_due_assignment,
636+
pastAssignments.size,
637+
pastAssignments.size
638+
)
639+
)
640+
}
641+
val futureAssignments = primaryCourse.courseAssignments?.futureAssignments
642+
if (!futureAssignments.isNullOrEmpty()) {
643+
val nearestAssignment = futureAssignments.minBy { it.date }
644+
val title = if (futureAssignments.size == 1) nearestAssignment.title else null
645+
Divider()
646+
AssignmentItem(
647+
modifier = Modifier.clickable {
648+
if (futureAssignments.size == 1) {
649+
resumeBlockId(primaryCourse, nearestAssignment.blockId)
650+
} else {
651+
navigateToDates(primaryCourse)
652+
}
653+
},
654+
painter = painterResource(id = CoreR.drawable.ic_core_chapter_icon),
655+
title = title,
656+
info = stringResource(
657+
R.string.dashboard_assignment_due,
658+
nearestAssignment.assignmentType ?: "",
659+
stringResource(
660+
id = CoreR.string.core_date_format_assignment_due,
661+
TimeUtils.formatToString(context, nearestAssignment.date, useRelativeDates),
662+
)
663+
)
636664
)
637665
}
666+
ResumeButton(
667+
primaryCourse = primaryCourse,
668+
onClick = {
669+
if (primaryCourse.courseStatus == null) {
670+
openCourse(primaryCourse)
671+
} else {
672+
resumeBlockId(
673+
primaryCourse,
674+
primaryCourse.courseStatus?.lastVisitedBlockId ?: ""
675+
)
676+
}
677+
}
678+
)
679+
}
680+
}
681+
682+
@Composable
683+
private fun PrimaryCourseCaption(
684+
modifier: Modifier = Modifier,
685+
primaryCourse: EnrolledCourse,
686+
imageHeight: Dp? = 140.dp,
687+
apiHostUrl: String,
688+
) {
689+
val context = LocalContext.current
690+
Column(modifier = modifier) {
691+
val imageModifier = imageHeight?.let {
692+
Modifier
693+
.height(it)
694+
.fillMaxWidth()
695+
} ?: Modifier
696+
.height(IntrinsicSize.Max)
697+
.fillMaxWidth()
698+
.weight(1f)
699+
700+
AsyncImage(
701+
model = ImageRequest.Builder(context)
702+
.data(primaryCourse.course.courseImage.toImageLink(apiHostUrl))
703+
.error(CoreR.drawable.core_no_image_course)
704+
.placeholder(CoreR.drawable.core_no_image_course)
705+
.build(),
706+
contentDescription = null,
707+
contentScale = ContentScale.Crop,
708+
modifier = imageModifier,
709+
)
710+
val progress: Float = try {
711+
primaryCourse.progress.assignmentsCompleted.toFloat() /
712+
primaryCourse.progress.totalAssignmentsCount.toFloat()
713+
} catch (_: ArithmeticException) {
714+
0f
715+
}
716+
LinearProgressIndicator(
717+
modifier = Modifier
718+
.fillMaxWidth()
719+
.height(8.dp),
720+
progress = progress,
721+
color = MaterialTheme.appColors.primary,
722+
backgroundColor = MaterialTheme.appColors.divider
723+
)
638724
}
639725
}
640726

@@ -704,7 +790,7 @@ private fun PrimaryCourseTitle(
704790
) {
705791
Column(
706792
modifier = modifier,
707-
verticalArrangement = Arrangement.spacedBy(4.dp)
793+
verticalArrangement = Arrangement.Center
708794
) {
709795
Text(
710796
modifier = Modifier.fillMaxWidth(),
@@ -713,15 +799,19 @@ private fun PrimaryCourseTitle(
713799
color = MaterialTheme.appColors.textFieldHint
714800
)
715801
Text(
716-
modifier = Modifier.fillMaxWidth(),
802+
modifier = Modifier
803+
.fillMaxWidth()
804+
.padding(top = 4.dp),
717805
text = primaryCourse.course.name,
718806
style = MaterialTheme.appTypography.titleLarge,
719807
color = MaterialTheme.appColors.textDark,
720808
overflow = TextOverflow.Ellipsis,
721809
maxLines = 3
722810
)
723811
Text(
724-
modifier = Modifier.fillMaxWidth(),
812+
modifier = Modifier
813+
.fillMaxWidth()
814+
.padding(top = 4.dp),
725815
style = MaterialTheme.appTypography.labelMedium,
726816
color = MaterialTheme.appColors.textFieldHint,
727817
text = TimeUtils.getCourseFormattedDate(

0 commit comments

Comments
 (0)