From 354bebbf3a47ebe0787a459f8cab68a03476f5a2 Mon Sep 17 00:00:00 2001 From: omerhabib26 Date: Thu, 18 Jan 2024 15:01:11 +0500 Subject: [PATCH 1/3] feat: added styling to CourseDateBlock - Added relative icons for each date type - Added locked content description - Improve code structuring - Improve time format to better understanding with date fix: LEARNER-9771 --- .../openedx/core/data/model/CourseDates.kt | 47 ++++---- .../org/openedx/core/data/model/DateType.kt | 17 +-- .../core/domain/model/CourseDateBlock.kt | 17 ++- .../java/org/openedx/core/utils/TimeUtils.kt | 58 +++++----- .../main/res/drawable/core_ic_assignment.xml | 12 ++ .../main/res/drawable/core_ic_certificate.xml | 9 ++ .../res/drawable/core_ic_course_expire.xml | 12 ++ core/src/main/res/drawable/core_ic_lock.xml | 9 ++ .../main/res/drawable/core_ic_start_end.xml | 12 ++ .../presentation/dates/CourseDatesFragment.kt | 104 +++++++++++++----- course/src/main/res/values/strings.xml | 1 + 11 files changed, 206 insertions(+), 92 deletions(-) create mode 100644 core/src/main/res/drawable/core_ic_assignment.xml create mode 100644 core/src/main/res/drawable/core_ic_certificate.xml create mode 100644 core/src/main/res/drawable/core_ic_course_expire.xml create mode 100644 core/src/main/res/drawable/core_ic_lock.xml create mode 100644 core/src/main/res/drawable/core_ic_start_end.xml diff --git a/core/src/main/java/org/openedx/core/data/model/CourseDates.kt b/core/src/main/java/org/openedx/core/data/model/CourseDates.kt index 5c27c44a4..2b01aa43a 100644 --- a/core/src/main/java/org/openedx/core/data/model/CourseDates.kt +++ b/core/src/main/java/org/openedx/core/data/model/CourseDates.kt @@ -4,7 +4,9 @@ import com.google.gson.annotations.SerializedName import org.openedx.core.domain.model.DatesSection import org.openedx.core.utils.TimeUtils import org.openedx.core.utils.addDays +import org.openedx.core.utils.clearTime import org.openedx.core.utils.isToday +import java.util.Date import org.openedx.core.domain.model.CourseDateBlock as DomainCourseDateBlock data class CourseDates( @@ -24,7 +26,7 @@ data class CourseDates( val verifiedUpgradeLink: String? = "", ) { fun getStructuredCourseDates(): LinkedHashMap> { - val currentDate = TimeUtils.getCurrentDate() + val currentDate = Date() val courseDatesResponse: LinkedHashMap> = LinkedHashMap() val datesList = mapToDomain() @@ -36,43 +38,44 @@ data class CourseDates( datesList.filter { currentDate.after(it.date) }.also { datesList.removeAll(it) } courseDatesResponse[DatesSection.TODAY] = - datesList.filter { it.date != null && it.date.isToday() } - .also { datesList.removeAll(it) } + datesList.filter { it.date.isToday() }.also { datesList.removeAll(it) } + + //Update the date for upcoming comparison without time + currentDate.clearTime() // for current week except today courseDatesResponse[DatesSection.THIS_WEEK] = datesList.filter { - it.date != null && it.date.after(currentDate) && - it.date.before(currentDate.addDays(8)) + it.date.after(currentDate) && it.date.before(currentDate.addDays(8)) }.also { datesList.removeAll(it) } // for coming week courseDatesResponse[DatesSection.NEXT_WEEK] = datesList.filter { - it.date != null && - it.date.after(currentDate.addDays(7)) && - it.date.before(currentDate.addDays(15)) + it.date.after(currentDate.addDays(7)) && it.date.before(currentDate.addDays(15)) }.also { datesList.removeAll(it) } // for upcoming courseDatesResponse[DatesSection.UPCOMING] = datesList.filter { - it.date != null && it.date.after(currentDate.addDays(14)) + it.date.after(currentDate.addDays(14)) }.also { datesList.removeAll(it) } return courseDatesResponse } private fun mapToDomain(): MutableList { - return courseDateBlocks.map { item -> - DomainCourseDateBlock( - title = item.title, - description = item.description, - link = item.link, - blockId = item.blockId, - date = TimeUtils.iso8601ToDate(item.date), - complete = item.complete, - learnerHasAccess = item.learnerHasAccess, - dateType = item.dateType, - assignmentType = item.assignmentType - ) - }.sortedBy { it.date }.filter { it.date != null }.toMutableList() + return courseDateBlocks.mapNotNull { item -> + TimeUtils.iso8601ToDate(item.date)?.let { date -> + DomainCourseDateBlock( + title = item.title, + description = item.description, + link = item.link, + blockId = item.blockId, + date = date, + complete = item.complete, + learnerHasAccess = item.learnerHasAccess, + dateType = item.dateType, + assignmentType = item.assignmentType + ) + } + }.sortedBy { it.date }.toMutableList() } } diff --git a/core/src/main/java/org/openedx/core/data/model/DateType.kt b/core/src/main/java/org/openedx/core/data/model/DateType.kt index e9af5256b..cca3ca304 100644 --- a/core/src/main/java/org/openedx/core/data/model/DateType.kt +++ b/core/src/main/java/org/openedx/core/data/model/DateType.kt @@ -1,31 +1,32 @@ package org.openedx.core.data.model import com.google.gson.annotations.SerializedName +import org.openedx.core.R -enum class DateType { +enum class DateType(val drawableResId: Int? = null) { @SerializedName("todays-date") TODAY_DATE, @SerializedName("course-start-date") - COURSE_START_DATE, + COURSE_START_DATE(R.drawable.core_ic_start_end), @SerializedName("course-end-date") - COURSE_END_DATE, + COURSE_END_DATE(R.drawable.core_ic_start_end), @SerializedName("course-expired-date") - COURSE_EXPIRED_DATE, + COURSE_EXPIRED_DATE(R.drawable.core_ic_course_expire), @SerializedName("assignment-due-date") - ASSIGNMENT_DUE_DATE, + ASSIGNMENT_DUE_DATE(R.drawable.core_ic_assignment), @SerializedName("certificate-available-date") - CERTIFICATE_AVAILABLE_DATE, + CERTIFICATE_AVAILABLE_DATE(R.drawable.core_ic_certificate), @SerializedName("verified-upgrade-deadline") - VERIFIED_UPGRADE_DEADLINE, + VERIFIED_UPGRADE_DEADLINE(R.drawable.core_ic_lock), @SerializedName("verification-deadline-date") - VERIFICATION_DEADLINE_DATE, + VERIFICATION_DEADLINE_DATE(R.drawable.core_ic_lock), NONE, } diff --git a/core/src/main/java/org/openedx/core/domain/model/CourseDateBlock.kt b/core/src/main/java/org/openedx/core/domain/model/CourseDateBlock.kt index 62a1ed81d..f248f0194 100644 --- a/core/src/main/java/org/openedx/core/domain/model/CourseDateBlock.kt +++ b/core/src/main/java/org/openedx/core/domain/model/CourseDateBlock.kt @@ -1,6 +1,8 @@ package org.openedx.core.domain.model import org.openedx.core.data.model.DateType +import org.openedx.core.utils.isTimeLessThan24Hours +import org.openedx.core.utils.isToday import java.util.Date data class CourseDateBlock( @@ -10,7 +12,7 @@ data class CourseDateBlock( val blockId: String = "", val learnerHasAccess: Boolean = false, val complete: Boolean = false, - val date: Date?, + val date: Date, val dateType: DateType = DateType.NONE, val assignmentType: String? = "", ) { @@ -20,6 +22,17 @@ data class CourseDateBlock( DateType.COURSE_END_DATE, DateType.CERTIFICATE_AVAILABLE_DATE, DateType.VERIFICATION_DEADLINE_DATE - ) && date?.before(Date()) == true) + ) && date.before(Date())) + } + + fun isLockedContent(): Boolean { + return dateType in setOf( + DateType.VERIFIED_UPGRADE_DEADLINE, + DateType.VERIFICATION_DEADLINE_DATE + ) + } + + fun isTimeDifferenceLessThan24Hours(): Boolean { + return (date.isToday() && date.before(Date())) || date.isTimeLessThan24Hours() } } diff --git a/core/src/main/java/org/openedx/core/utils/TimeUtils.kt b/core/src/main/java/org/openedx/core/utils/TimeUtils.kt index 9964e9705..11b3f6cc8 100644 --- a/core/src/main/java/org/openedx/core/utils/TimeUtils.kt +++ b/core/src/main/java/org/openedx/core/utils/TimeUtils.kt @@ -22,15 +22,6 @@ object TimeUtils { private const val SEVEN_DAYS_IN_MILLIS = 604800000L - /** - * This method used to get the current date - * @return The current date with time set to midnight. - */ - fun getCurrentDate(): Date { - val calendar = Calendar.getInstance().also { it.clearTimeComponents() } - return calendar.time - } - fun getCurrentTime(): Long { return Calendar.getInstance().timeInMillis } @@ -53,15 +44,6 @@ object TimeUtils { } } - /** - * This method used to convert the date to ISO 8601 compliant format date string - * @param date [Date]needs to be converted - * @return The current date and time in a ISO 8601 compliant format. - */ - fun dateToIso8601(date: Date?): String { - return ISO8601Utils.format(date, true) - } - fun iso8601ToDateWithTime(context: Context, text: String): String { return try { val courseDateFormat = SimpleDateFormat(FORMAT_ISO_8601, Locale.getDefault()) @@ -75,17 +57,13 @@ object TimeUtils { } } - fun dateToCourseDate(resourceManager: ResourceManager, date: Date?): String { + private fun dateToCourseDate(resourceManager: ResourceManager, date: Date?): String { return formatDate( format = resourceManager.getString(R.string.core_date_format_MMMM_dd), date = date ) } - fun formatDate(format: String, date: String): String { - return formatDate(format, iso8601ToDate(date)) - } - - fun formatDate(format: String, date: Date?): String { + private fun formatDate(format: String, date: Date?): String { if (date == null) { return "" } @@ -93,13 +71,6 @@ object TimeUtils { return sdf.format(date) } - fun stringToDate(dateFormat: String, date: String): Date? { - if (dateFormat.isEmpty() || date.isEmpty()) { - return null - } - return SimpleDateFormat(dateFormat, Locale.getDefault()).parse(date) - } - /** * Checks if the given date is past today. * @@ -200,6 +171,15 @@ object TimeUtils { return formattedDate } + fun getFormattedTime(date: Date): String { + return DateUtils.getRelativeTimeSpanString( + date.time, + getCurrentTime(), + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_TIME + ).toString() + } + /** * Returns a formatted date string for the given date. */ @@ -279,7 +259,7 @@ fun Date.isToday(): Boolean { val calendar = Calendar.getInstance() calendar.time = this calendar.clearTimeComponents() - return calendar.time == TimeUtils.getCurrentDate() + return calendar.time == Date().clearTime() } // Extension function to add number of days to a date @@ -290,3 +270,17 @@ fun Date.addDays(days: Int): Date { calendar.add(Calendar.DATE, days) return calendar.time } + +fun Date.clearTime(): Date { + val calendar = Calendar.getInstance() + calendar.time = this + calendar.clearTimeComponents() + return calendar.time +} + +fun Date.isTimeLessThan24Hours(): Boolean { + val calendar = Calendar.getInstance() + calendar.time = this + val timeInMillis = (calendar.timeInMillis - TimeUtils.getCurrentTime()).unaryPlus() + return timeInMillis < TimeUnit.DAYS.toMillis(1) +} diff --git a/core/src/main/res/drawable/core_ic_assignment.xml b/core/src/main/res/drawable/core_ic_assignment.xml new file mode 100644 index 000000000..fdd07fd45 --- /dev/null +++ b/core/src/main/res/drawable/core_ic_assignment.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/core/src/main/res/drawable/core_ic_certificate.xml b/core/src/main/res/drawable/core_ic_certificate.xml new file mode 100644 index 000000000..917b002f7 --- /dev/null +++ b/core/src/main/res/drawable/core_ic_certificate.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/src/main/res/drawable/core_ic_course_expire.xml b/core/src/main/res/drawable/core_ic_course_expire.xml new file mode 100644 index 000000000..f9e6a11ad --- /dev/null +++ b/core/src/main/res/drawable/core_ic_course_expire.xml @@ -0,0 +1,12 @@ + + + + diff --git a/core/src/main/res/drawable/core_ic_lock.xml b/core/src/main/res/drawable/core_ic_lock.xml new file mode 100644 index 000000000..d0e0ba4c7 --- /dev/null +++ b/core/src/main/res/drawable/core_ic_lock.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/src/main/res/drawable/core_ic_start_end.xml b/core/src/main/res/drawable/core_ic_start_end.xml new file mode 100644 index 000000000..e881ea5ca --- /dev/null +++ b/core/src/main/res/drawable/core_ic_start_end.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt index 45995eed6..ef3506d53 100644 --- a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt +++ b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt @@ -45,9 +45,13 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -56,6 +60,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -73,19 +78,28 @@ import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import org.openedx.core.UIMessage +import org.openedx.core.data.model.DateType import org.openedx.core.domain.model.CourseDateBlock import org.openedx.core.domain.model.DatesSection import org.openedx.core.extension.isNotEmptyThenLet import org.openedx.core.presentation.course.CourseViewMode -import org.openedx.core.ui.* +import org.openedx.core.ui.HandleUIMessage +import org.openedx.core.ui.OfflineModeDialog +import org.openedx.core.ui.WindowSize +import org.openedx.core.ui.WindowType +import org.openedx.core.ui.displayCutoutForLandscape +import org.openedx.core.ui.rememberWindowSize +import org.openedx.core.ui.statusBarsInset import org.openedx.core.ui.theme.OpenEdXTheme import org.openedx.core.ui.theme.appColors import org.openedx.core.ui.theme.appShapes import org.openedx.core.ui.theme.appTypography -import org.openedx.core.R as coreR +import org.openedx.core.ui.windowSizeValue import org.openedx.core.utils.TimeUtils +import org.openedx.core.utils.clearTime import org.openedx.course.R import org.openedx.course.presentation.CourseRouter +import org.openedx.core.R as coreR class CourseDatesFragment : Fragment() { @@ -433,7 +447,7 @@ private fun CourseDateBlockSection( if (sectionKey != DatesSection.COMPLETED) { DateBullet(section = sectionKey) } - DateBlock(section = sectionKey, sectionDates, onItemClick) + DateBlock(dateBlocks = sectionDates, onItemClick = onItemClick) } } } @@ -464,7 +478,6 @@ private fun DateBullet( @Composable private fun DateBlock( - section: DatesSection = DatesSection.NONE, dateBlocks: List, onItemClick: (String) -> Unit, ) { @@ -474,9 +487,9 @@ private fun DateBlock( .wrapContentHeight() .padding(start = 8.dp, end = 8.dp), ) { - var lastAssignmentDate = dateBlocks.firstOrNull()?.date - var canShowDate = (section == DatesSection.TODAY).not() + var lastAssignmentDate = dateBlocks.first().date.clearTime() dateBlocks.forEachIndexed { index, dateBlock -> + var canShowDate = index == 0 if (index != 0) { canShowDate = (lastAssignmentDate != dateBlock.date) } @@ -501,9 +514,14 @@ private fun CourseDateItem( if (isMiddleChild) { Spacer(modifier = Modifier.height(20.dp)) } - if (canShowDate && dateBlock.date != null) { + if (canShowDate) { + val timeTitle = if (dateBlock.isTimeDifferenceLessThan24Hours()) { + TimeUtils.getFormattedTime(dateBlock.date) + } else { + TimeUtils.getCourseFormattedDate(LocalContext.current, dateBlock.date) + } Text( - text = TimeUtils.getCourseFormattedDate(LocalContext.current, dateBlock.date!!), + text = timeTitle, style = TextStyle( fontSize = 12.sp, lineHeight = 16.sp, @@ -517,13 +535,24 @@ private fun CourseDateItem( Row( modifier = Modifier .fillMaxWidth() + .padding(end = 4.dp) .clickable(enabled = dateBlock.blockId.isNotEmpty() && dateBlock.learnerHasAccess, onClick = { onItemClick(dateBlock.blockId) }) ) { + dateBlock.dateType.drawableResId?.let { icon -> + Icon( + modifier = Modifier + .padding(end = 4.dp) + .align(Alignment.CenterVertically), + painter = painterResource(id = icon), + contentDescription = null, + tint = MaterialTheme.appColors.textDark + ) + } Text( modifier = Modifier .weight(1f) - .padding(end = 8.dp), + .align(Alignment.CenterVertically), // append assignment type if available with title text = if (dateBlock.assignmentType.isNullOrEmpty().not()) { "${dateBlock.assignmentType}: ${dateBlock.title}" @@ -537,10 +566,10 @@ private fun CourseDateItem( color = MaterialTheme.appColors.textDark, letterSpacing = 0.15.sp, ), - maxLines = 2, + maxLines = 1, overflow = TextOverflow.Ellipsis, ) - Spacer(modifier = Modifier.width(16.dp)) + Spacer(modifier = Modifier.width(7.dp)) if (dateBlock.blockId.isNotEmpty() && dateBlock.learnerHasAccess) { Icon( @@ -553,6 +582,21 @@ private fun CourseDateItem( ) } } + if (dateBlock.isLockedContent()) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(top = 4.dp), + text = stringResource(id = R.string.course_dates_locked_content_description), + style = TextStyle( + fontSize = 11.sp, + lineHeight = 16.sp, + fontWeight = FontWeight.Normal, + color = MaterialTheme.appColors.textFieldBorder, + letterSpacing = 0.5.sp, + ), + ) + } } } @@ -593,57 +637,60 @@ private val mockedResponse: LinkedHashMap> = Pair( DatesSection.COMPLETED, listOf( CourseDateBlock( - title = "Homework 1: ABCD", + title = "Homework 1: A B C D", description = "After this date, course content will be archived", - date = TimeUtils.iso8601ToDate("2023-10-20T15:08:07Z"), + date = TimeUtils.iso8601ToDate("2023-10-20T15:08:07Z")!!, ) ) ), Pair( DatesSection.COMPLETED, listOf( CourseDateBlock( - title = "Homework 1: ABCD", + title = "Homework 1: A B C D", description = "After this date, course content will be archived", - date = TimeUtils.iso8601ToDate("2023-10-20T15:08:07Z"), + date = TimeUtils.iso8601ToDate("2023-10-20T15:08:07Z")!!, ) ) ), Pair( DatesSection.PAST_DUE, listOf( CourseDateBlock( - title = "Homework 1: ABCD", + title = "Homework 1: A B C D", description = "After this date, course content will be archived", - date = TimeUtils.iso8601ToDate("2023-10-20T15:08:07Z"), + date = TimeUtils.iso8601ToDate("2023-10-20T15:08:07Z")!!, + dateType = DateType.ASSIGNMENT_DUE_DATE, ) ) ), Pair( DatesSection.TODAY, listOf( CourseDateBlock( - title = "Homework 2: ABCD", + title = "Homework 2: A B C D", description = "After this date, course content will be archived", - date = TimeUtils.iso8601ToDate("2023-10-21T15:08:07Z"), + date = TimeUtils.iso8601ToDate("2023-10-21T15:08:07Z")!!, ) ) ), Pair( DatesSection.THIS_WEEK, listOf( CourseDateBlock( - title = "Assignment Due: ABCD", + title = "Assignment Due: A B C D", description = "After this date, course content will be archived", - date = TimeUtils.iso8601ToDate("2023-10-22T15:08:07Z"), + date = TimeUtils.iso8601ToDate("2023-10-22T15:08:07Z")!!, + dateType = DateType.ASSIGNMENT_DUE_DATE, ), CourseDateBlock( title = "Assignment Due", description = "After this date, course content will be archived", - date = TimeUtils.iso8601ToDate("2023-10-23T15:08:07Z"), + date = TimeUtils.iso8601ToDate("2023-10-23T15:08:07Z")!!, + dateType = DateType.ASSIGNMENT_DUE_DATE, ), CourseDateBlock( title = "Surprise Assignment", description = "After this date, course content will be archived", - date = TimeUtils.iso8601ToDate("2023-10-24T15:08:07Z"), + date = TimeUtils.iso8601ToDate("2023-10-24T15:08:07Z")!!, ) ) ), Pair( DatesSection.NEXT_WEEK, listOf( CourseDateBlock( - title = "Homework 5: ABCD", + title = "Homework 5: A B C D", description = "After this date, course content will be archived", - date = TimeUtils.iso8601ToDate("2023-10-25T15:08:07Z"), + date = TimeUtils.iso8601ToDate("2023-10-25T15:08:07Z")!!, ) ) ), Pair( @@ -651,8 +698,9 @@ private val mockedResponse: LinkedHashMap> = CourseDateBlock( title = "Last Assignment", description = "After this date, course content will be archived", - date = TimeUtils.iso8601ToDate("2023-10-26T15:08:07Z"), - assignmentType = "Module 1" + date = TimeUtils.iso8601ToDate("2023-10-26T15:08:07Z")!!, + assignmentType = "Module 1", + dateType = DateType.VERIFICATION_DEADLINE_DATE, ) ) ) diff --git a/course/src/main/res/values/strings.xml b/course/src/main/res/values/strings.xml index cf64be401..316df2537 100644 --- a/course/src/main/res/values/strings.xml +++ b/course/src/main/res/values/strings.xml @@ -55,6 +55,7 @@ Course dates are not currently available. + You must successfully complete verification before this date to qualify for a Verified Certificate. Header image for %1$s From f5d869db261ee8f4ed65e004b06403d5c34b149a Mon Sep 17 00:00:00 2001 From: omerhabib26 Date: Tue, 23 Jan 2024 03:43:47 +0500 Subject: [PATCH 2/3] fix: minor improvements --- .../src/main/java/org/openedx/core/data/model/DateType.kt | 4 ++-- .../course/presentation/dates/CourseDatesFragment.kt | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/openedx/core/data/model/DateType.kt b/core/src/main/java/org/openedx/core/data/model/DateType.kt index cca3ca304..a5081fc77 100644 --- a/core/src/main/java/org/openedx/core/data/model/DateType.kt +++ b/core/src/main/java/org/openedx/core/data/model/DateType.kt @@ -23,10 +23,10 @@ enum class DateType(val drawableResId: Int? = null) { CERTIFICATE_AVAILABLE_DATE(R.drawable.core_ic_certificate), @SerializedName("verified-upgrade-deadline") - VERIFIED_UPGRADE_DEADLINE(R.drawable.core_ic_lock), + VERIFIED_UPGRADE_DEADLINE(R.drawable.core_ic_start_end), @SerializedName("verification-deadline-date") - VERIFICATION_DEADLINE_DATE(R.drawable.core_ic_lock), + VERIFICATION_DEADLINE_DATE(R.drawable.core_ic_start_end), NONE, } diff --git a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt index ef3506d53..44c2867c4 100644 --- a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt +++ b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt @@ -544,7 +544,7 @@ private fun CourseDateItem( modifier = Modifier .padding(end = 4.dp) .align(Alignment.CenterVertically), - painter = painterResource(id = icon), + painter = painterResource(id = if (dateBlock.learnerHasAccess.not()) coreR.drawable.core_ic_lock else icon), contentDescription = null, tint = MaterialTheme.appColors.textDark ) @@ -582,17 +582,17 @@ private fun CourseDateItem( ) } } - if (dateBlock.isLockedContent()) { + if (dateBlock.description.isNotEmpty()) { Text( modifier = Modifier .fillMaxWidth() .padding(top = 4.dp), - text = stringResource(id = R.string.course_dates_locked_content_description), + text = dateBlock.description, style = TextStyle( fontSize = 11.sp, lineHeight = 16.sp, fontWeight = FontWeight.Normal, - color = MaterialTheme.appColors.textFieldBorder, + color = MaterialTheme.appColors.textPrimaryVariant, letterSpacing = 0.5.sp, ), ) From 869bdd0cfe9d7fdc5e0315b69e286f4bc0abd442 Mon Sep 17 00:00:00 2001 From: omerhabib26 Date: Wed, 24 Jan 2024 01:26:31 +0500 Subject: [PATCH 3/3] fix: Address PR comments --- .../org/openedx/core/data/model/DateType.kt | 6 ++--- .../core/domain/model/CourseDateBlock.kt | 10 ++----- .../java/org/openedx/core/utils/TimeUtils.kt | 27 ++++++++++++++++--- .../main/res/drawable/core_ic_calendar.xml | 9 +++++++ .../presentation/dates/CourseDatesFragment.kt | 14 +++++----- course/src/main/res/values/strings.xml | 1 - 6 files changed, 45 insertions(+), 22 deletions(-) create mode 100644 core/src/main/res/drawable/core_ic_calendar.xml diff --git a/core/src/main/java/org/openedx/core/data/model/DateType.kt b/core/src/main/java/org/openedx/core/data/model/DateType.kt index a5081fc77..8604286d7 100644 --- a/core/src/main/java/org/openedx/core/data/model/DateType.kt +++ b/core/src/main/java/org/openedx/core/data/model/DateType.kt @@ -5,7 +5,7 @@ import org.openedx.core.R enum class DateType(val drawableResId: Int? = null) { @SerializedName("todays-date") - TODAY_DATE, + TODAY_DATE(R.drawable.core_ic_calendar), @SerializedName("course-start-date") COURSE_START_DATE(R.drawable.core_ic_start_end), @@ -23,10 +23,10 @@ enum class DateType(val drawableResId: Int? = null) { CERTIFICATE_AVAILABLE_DATE(R.drawable.core_ic_certificate), @SerializedName("verified-upgrade-deadline") - VERIFIED_UPGRADE_DEADLINE(R.drawable.core_ic_start_end), + VERIFIED_UPGRADE_DEADLINE(R.drawable.core_ic_calendar), @SerializedName("verification-deadline-date") - VERIFICATION_DEADLINE_DATE(R.drawable.core_ic_start_end), + VERIFICATION_DEADLINE_DATE(R.drawable.core_ic_calendar), NONE, } diff --git a/core/src/main/java/org/openedx/core/domain/model/CourseDateBlock.kt b/core/src/main/java/org/openedx/core/domain/model/CourseDateBlock.kt index f248f0194..7e91c59fa 100644 --- a/core/src/main/java/org/openedx/core/domain/model/CourseDateBlock.kt +++ b/core/src/main/java/org/openedx/core/domain/model/CourseDateBlock.kt @@ -21,15 +21,9 @@ data class CourseDateBlock( DateType.COURSE_START_DATE, DateType.COURSE_END_DATE, DateType.CERTIFICATE_AVAILABLE_DATE, - DateType.VERIFICATION_DEADLINE_DATE - ) && date.before(Date())) - } - - fun isLockedContent(): Boolean { - return dateType in setOf( DateType.VERIFIED_UPGRADE_DEADLINE, - DateType.VERIFICATION_DEADLINE_DATE - ) + DateType.VERIFICATION_DEADLINE_DATE, + ) && date.before(Date())) } fun isTimeDifferenceLessThan24Hours(): Boolean { diff --git a/core/src/main/java/org/openedx/core/utils/TimeUtils.kt b/core/src/main/java/org/openedx/core/utils/TimeUtils.kt index 11b3f6cc8..e85397491 100644 --- a/core/src/main/java/org/openedx/core/utils/TimeUtils.kt +++ b/core/src/main/java/org/openedx/core/utils/TimeUtils.kt @@ -171,6 +171,12 @@ object TimeUtils { return formattedDate } + /** + * Method to get the formatted time string in terms of relative time with minimum resolution of minutes. + * For example, if the time difference is 1 minute, it will return "1m ago". + * + * @param date Date object to be formatted. + */ fun getFormattedTime(date: Date): String { return DateUtils.getRelativeTimeSpanString( date.time, @@ -246,7 +252,10 @@ object TimeUtils { } } -// Extension function to clear time components +/** + * Extension function to clear time components of a calendar. + * for example, if the time is 10:30:45, it will set the time to 00:00:00 + */ fun Calendar.clearTimeComponents() { this.set(Calendar.HOUR_OF_DAY, 0) this.set(Calendar.MINUTE, 0) @@ -254,7 +263,9 @@ fun Calendar.clearTimeComponents() { this.set(Calendar.MILLISECOND, 0) } -// Extension function to check if a date is today +/** + * Extension function to check if the given date is today. + */ fun Date.isToday(): Boolean { val calendar = Calendar.getInstance() calendar.time = this @@ -262,7 +273,10 @@ fun Date.isToday(): Boolean { return calendar.time == Date().clearTime() } -// Extension function to add number of days to a date +/** + * Extension function to add days to a date. + * for example, if the date is 2020-01-01 10:30:45, and days is 2, it will return 2020-01-03 00:00:00 + */ fun Date.addDays(days: Int): Date { val calendar = Calendar.getInstance() calendar.time = this @@ -271,6 +285,10 @@ fun Date.addDays(days: Int): Date { return calendar.time } +/** + * Extension function to clear time components of a date. + * for example, if the date is 2020-01-01 10:30:45, it will return 2020-01-01 00:00:00 + */ fun Date.clearTime(): Date { val calendar = Calendar.getInstance() calendar.time = this @@ -278,6 +296,9 @@ fun Date.clearTime(): Date { return calendar.time } +/** + * Extension function to check if the time difference between the given date and the current date is less than 24 hours. + */ fun Date.isTimeLessThan24Hours(): Boolean { val calendar = Calendar.getInstance() calendar.time = this diff --git a/core/src/main/res/drawable/core_ic_calendar.xml b/core/src/main/res/drawable/core_ic_calendar.xml new file mode 100644 index 000000000..8dcd8c896 --- /dev/null +++ b/core/src/main/res/drawable/core_ic_calendar.xml @@ -0,0 +1,9 @@ + + + diff --git a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt index 44c2867c4..53a959a9e 100644 --- a/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt +++ b/course/src/main/java/org/openedx/course/presentation/dates/CourseDatesFragment.kt @@ -343,7 +343,7 @@ fun ExpandableView( modifier = Modifier .fillMaxWidth() .background(MaterialTheme.appColors.cardViewBackground, MaterialTheme.shapes.medium) - .border(2.dp, MaterialTheme.appColors.cardViewBorder, MaterialTheme.shapes.medium) + .border(0.75.dp, MaterialTheme.appColors.cardViewBorder, MaterialTheme.shapes.medium) ) { Row(modifier = Modifier .fillMaxWidth() @@ -637,7 +637,7 @@ private val mockedResponse: LinkedHashMap> = Pair( DatesSection.COMPLETED, listOf( CourseDateBlock( - title = "Homework 1: A B C D", + title = "Homework 1: ABCD", description = "After this date, course content will be archived", date = TimeUtils.iso8601ToDate("2023-10-20T15:08:07Z")!!, ) @@ -645,7 +645,7 @@ private val mockedResponse: LinkedHashMap> = ), Pair( DatesSection.COMPLETED, listOf( CourseDateBlock( - title = "Homework 1: A B C D", + title = "Homework 1: ABCD", description = "After this date, course content will be archived", date = TimeUtils.iso8601ToDate("2023-10-20T15:08:07Z")!!, ) @@ -653,7 +653,7 @@ private val mockedResponse: LinkedHashMap> = ), Pair( DatesSection.PAST_DUE, listOf( CourseDateBlock( - title = "Homework 1: A B C D", + title = "Homework 1: ABCD", description = "After this date, course content will be archived", date = TimeUtils.iso8601ToDate("2023-10-20T15:08:07Z")!!, dateType = DateType.ASSIGNMENT_DUE_DATE, @@ -662,7 +662,7 @@ private val mockedResponse: LinkedHashMap> = ), Pair( DatesSection.TODAY, listOf( CourseDateBlock( - title = "Homework 2: A B C D", + title = "Homework 2: ABCD", description = "After this date, course content will be archived", date = TimeUtils.iso8601ToDate("2023-10-21T15:08:07Z")!!, ) @@ -670,7 +670,7 @@ private val mockedResponse: LinkedHashMap> = ), Pair( DatesSection.THIS_WEEK, listOf( CourseDateBlock( - title = "Assignment Due: A B C D", + title = "Assignment Due: ABCD", description = "After this date, course content will be archived", date = TimeUtils.iso8601ToDate("2023-10-22T15:08:07Z")!!, dateType = DateType.ASSIGNMENT_DUE_DATE, @@ -688,7 +688,7 @@ private val mockedResponse: LinkedHashMap> = ), Pair( DatesSection.NEXT_WEEK, listOf( CourseDateBlock( - title = "Homework 5: A B C D", + title = "Homework 5: ABCD", description = "After this date, course content will be archived", date = TimeUtils.iso8601ToDate("2023-10-25T15:08:07Z")!!, ) diff --git a/course/src/main/res/values/strings.xml b/course/src/main/res/values/strings.xml index 316df2537..cf64be401 100644 --- a/course/src/main/res/values/strings.xml +++ b/course/src/main/res/values/strings.xml @@ -55,7 +55,6 @@ Course dates are not currently available. - You must successfully complete verification before this date to qualify for a Verified Certificate. Header image for %1$s