From f577f3f9258cafa0ebae395d12d2fcdcc7fa84ae Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Thu, 12 Sep 2024 15:53:05 +0530 Subject: [PATCH 1/6] feat: hide some options for the MVP (#1) * feat: hide some menu options * chore: update dev config (cherry picked from commit 930350569b6972584db523af70fb56af43a34558) (cherry picked from commit c6bc3ad299c5f689b17053ff12b4554ffb31781c) --- .../main/java/org/openedx/app/MainFragment.kt | 25 +++--------------- .../java/org/openedx/app/MainViewModel.kt | 8 +++--- app/src/main/res/menu/bottom_view_menu.xml | 6 ----- .../core/config/AgreementUrlsConfig.kt | 4 +++ .../core/domain/model/AgreementUrls.kt | 1 + core/src/main/res/values/strings.xml | 1 + default_config/dev/config.yaml | 3 ++- .../profile/compose/ProfileView.kt | 12 --------- .../presentation/settings/SettingsFragment.kt | 4 ++- .../presentation/settings/SettingsScreenUI.kt | 26 +------------------ .../settings/SettingsViewModel.kt | 11 ++++---- 11 files changed, 25 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/org/openedx/app/MainFragment.kt b/app/src/main/java/org/openedx/app/MainFragment.kt index 3ab735d27..6eca5da40 100644 --- a/app/src/main/java/org/openedx/app/MainFragment.kt +++ b/app/src/main/java/org/openedx/app/MainFragment.kt @@ -50,11 +50,6 @@ class MainFragment : Fragment(R.layout.fragment_main) { binding.viewPager.setCurrentItem(0, false) } - R.id.fragmentDiscover -> { - viewModel.logDiscoveryTabClickedEvent() - binding.viewPager.setCurrentItem(1, false) - } - R.id.fragmentProfile -> { viewModel.logProfileTabClickedEvent() binding.viewPager.setCurrentItem(2, false) @@ -68,9 +63,9 @@ class MainFragment : Fragment(R.layout.fragment_main) { } viewLifecycleOwner.lifecycleScope.launch { - viewModel.navigateToDiscovery.collect { shouldNavigateToDiscovery -> - if (shouldNavigateToDiscovery) { - binding.bottomNavView.selectedItemId = R.id.fragmentDiscover + viewModel.navigateToHome.collect { shouldNavigateToHome -> + if (shouldNavigateToHome) { + binding.bottomNavView.selectedItemId = R.id.fragmentLearn } } } @@ -92,13 +87,6 @@ class MainFragment : Fragment(R.layout.fragment_main) { when (requireArguments().getString(ARG_OPEN_TAB, "")) { HomeTab.LEARN.name, - HomeTab.PROGRAMS.name -> { - binding.bottomNavView.selectedItemId = R.id.fragmentLearn - } - - HomeTab.DISCOVER.name -> { - binding.bottomNavView.selectedItemId = R.id.fragmentDiscover - } HomeTab.PROFILE.name -> { binding.bottomNavView.selectedItemId = R.id.fragmentProfile @@ -114,14 +102,9 @@ class MainFragment : Fragment(R.layout.fragment_main) { binding.viewPager.offscreenPageLimit = 4 val openTab = requireArguments().getString(ARG_OPEN_TAB, HomeTab.LEARN.name) - val learnTab = if (openTab == HomeTab.PROGRAMS.name) { - LearnTab.PROGRAMS - } else { - LearnTab.COURSES - } + val learnTab = LearnTab.COURSES adapter = NavigationFragmentAdapter(this).apply { addFragment(LearnFragment.newInstance(openTab = learnTab.name)) - addFragment(viewModel.getDiscoveryFragment) addFragment(ProfileFragment()) } binding.viewPager.adapter = adapter diff --git a/app/src/main/java/org/openedx/app/MainViewModel.kt b/app/src/main/java/org/openedx/app/MainViewModel.kt index 69c809b5c..8708b6341 100644 --- a/app/src/main/java/org/openedx/app/MainViewModel.kt +++ b/app/src/main/java/org/openedx/app/MainViewModel.kt @@ -26,9 +26,9 @@ class MainViewModel( val isBottomBarEnabled: LiveData get() = _isBottomBarEnabled - private val _navigateToDiscovery = MutableSharedFlow() - val navigateToDiscovery: SharedFlow - get() = _navigateToDiscovery.asSharedFlow() + private val _navigateToHome = MutableSharedFlow() + val navigateToHome: SharedFlow + get() = _navigateToHome.asSharedFlow() val isDiscoveryTypeWebView get() = config.getDiscoveryConfig().isViewTypeWebView() val getDiscoveryFragment get() = DiscoveryNavigator(isDiscoveryTypeWebView).getDiscoveryFragment() @@ -38,7 +38,7 @@ class MainViewModel( notifier.notifier .onEach { if (it is NavigationToDiscovery) { - _navigateToDiscovery.emit(true) + _navigateToHome.emit(true) } } .distinctUntilChanged() diff --git a/app/src/main/res/menu/bottom_view_menu.xml b/app/src/main/res/menu/bottom_view_menu.xml index f97e849f7..730553f88 100644 --- a/app/src/main/res/menu/bottom_view_menu.xml +++ b/app/src/main/res/menu/bottom_view_menu.xml @@ -7,12 +7,6 @@ android:icon="@drawable/app_ic_rows" android:title="@string/app_navigation_learn" /> - - Do not sell my personal information View FAQ Terms of Use + Contact Support Profile Cancel Search diff --git a/default_config/dev/config.yaml b/default_config/dev/config.yaml index 4d1d694ec..33edbd75b 100644 --- a/default_config/dev/config.yaml +++ b/default_config/dev/config.yaml @@ -11,7 +11,8 @@ AGREEMENT_URLS: PRIVACY_POLICY_URL: '' COOKIE_POLICY_URL: '' DATA_SELL_CONSENT_URL: '' - TOS_URL: '' + CONTACT_SUPPORT_URL: 'https://eshemoeasu.my.site.com/help/s/' + TOS_URL: 'https://courses.ethernet.edu.et/terms' EULA_URL: '' SUPPORTED_LANGUAGES: [ ] #en is default language diff --git a/profile/src/main/java/org/openedx/profile/presentation/profile/compose/ProfileView.kt b/profile/src/main/java/org/openedx/profile/presentation/profile/compose/ProfileView.kt index e897b37c6..331e7c3d1 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/profile/compose/ProfileView.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/profile/compose/ProfileView.kt @@ -38,7 +38,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import org.openedx.core.R import org.openedx.core.ui.HandleUIMessage -import org.openedx.core.ui.OpenEdXOutlinedButton import org.openedx.core.ui.Toolbar import org.openedx.core.ui.displayCutoutForLandscape import org.openedx.core.ui.statusBarsInset @@ -143,17 +142,6 @@ internal fun ProfileView( subtitle = "@${uiState.account.username}" ) ProfileInfoSection(uiState.account) - OpenEdXOutlinedButton( - modifier = Modifier - .fillMaxWidth(), - text = stringResource(id = org.openedx.profile.R.string.profile_edit_profile), - onClick = { - onAction(ProfileViewAction.EditAccountClick) - }, - borderColor = MaterialTheme.appColors.primaryButtonBackground, - textColor = MaterialTheme.appColors.textAccent - ) - Spacer(modifier = Modifier.height(12.dp)) } } } diff --git a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsFragment.kt index 217a35258..75763bbc4 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsFragment.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsFragment.kt @@ -68,7 +68,9 @@ class SettingsFragment : Fragment() { SettingsScreenAction.FaqClick -> viewModel.faqClicked() SettingsScreenAction.SupportClick -> { - viewModel.emailSupportClicked(requireContext()) + viewModel.emailSupportClicked( + requireActivity().supportFragmentManager + ) } SettingsScreenAction.TermsClick -> { diff --git a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsScreenUI.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsScreenUI.kt index 68c773745..4b50b8f5d 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsScreenUI.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsScreenUI.kt @@ -167,12 +167,6 @@ internal fun SettingsScreen( .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally ) { - Spacer(Modifier.height(30.dp)) - - ManageAccountSection(onManageAccountClick = { - onAction(SettingsScreenAction.ManageAccountClick) - }) - Spacer(modifier = Modifier.height(24.dp)) SettingsSection( @@ -243,24 +237,6 @@ private fun SettingsSection( } } -@Composable -private fun ManageAccountSection(onManageAccountClick: () -> Unit) { - Column { - Card( - shape = MaterialTheme.appShapes.cardShape, - elevation = 0.dp, - backgroundColor = MaterialTheme.appColors.cardViewBackground - ) { - Column(Modifier.fillMaxWidth()) { - SettingsItem( - text = stringResource(id = R.string.core_manage_account), - onClick = onManageAccountClick - ) - } - } - } -} - @Composable private fun SupportInfoSection( uiState: SettingsUIState.Data, @@ -282,7 +258,7 @@ private fun SupportInfoSection( backgroundColor = MaterialTheme.appColors.cardViewBackground ) { Column(Modifier.fillMaxWidth()) { - if (uiState.configuration.supportEmail.isNotBlank()) { + if (uiState.configuration.agreementUrls.contactSupportUrl.isNotBlank()) { SettingsItem(text = stringResource(id = profileR.string.profile_contact_support)) { onAction(SettingsScreenAction.SupportClick) } diff --git a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsViewModel.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsViewModel.kt index 59548d1c9..37cae36ee 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsViewModel.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/settings/SettingsViewModel.kt @@ -23,7 +23,6 @@ import org.openedx.core.system.AppCookieManager import org.openedx.core.system.notifier.app.AppNotifier import org.openedx.core.system.notifier.app.AppUpgradeEvent import org.openedx.core.system.notifier.app.LogoutEvent -import org.openedx.core.utils.EmailUtil import org.openedx.foundation.extension.isInternetError import org.openedx.foundation.presentation.BaseViewModel import org.openedx.foundation.presentation.UIMessage @@ -182,11 +181,11 @@ class SettingsViewModel( logProfileEvent(ProfileAnalyticsEvent.TERMS_OF_USE_CLICKED) } - fun emailSupportClicked(context: Context) { - EmailUtil.showFeedbackScreen( - context = context, - feedbackEmailAddress = config.getFeedbackEmailAddress(), - appVersion = appData.versionName + fun emailSupportClicked(fragmentManager: FragmentManager) { + profileRouter.navigateToWebContent( + fm = fragmentManager, + title = resourceManager.getString(R.string.core_contact_support), + url = configuration.agreementUrls.contactSupportUrl, ) logProfileEvent(ProfileAnalyticsEvent.CONTACT_SUPPORT_CLICKED) } From 05d850d17d6d75cabf3002fb5a8727b59e8fb9f3 Mon Sep 17 00:00:00 2001 From: Demid Date: Thu, 17 Oct 2024 13:56:55 +0300 Subject: [PATCH 2/6] feat: add "Gated" UI state for subsections (cherry picked from commit 881b73bd0f99f0c0a9a00f21337f5a3e7b68d23b) (cherry picked from commit 02bc3da82196be84beaca30470c928cfd9a58a43) --- .../org/openedx/core/data/api/CourseApi.kt | 4 ++ .../core/data/model/GatedContentModel.kt | 27 ++++++++ .../openedx/core/data/model/SequenceModel.kt | 30 +++++++++ .../openedx/core/domain/model/GatedContent.kt | 13 ++++ .../openedx/core/domain/model/Subsection.kt | 14 +++++ .../data/repository/CourseRepository.kt | 2 + .../domain/interactor/CourseInteractor.kt | 2 + .../section/CourseSectionFragment.kt | 30 +++++++++ .../section/CourseSectionUIState.kt | 4 ++ .../section/CourseSectionViewModel.kt | 8 +++ course/src/main/res/values/strings.xml | 1 + .../section/CourseSectionViewModelTest.kt | 62 ++++++++++++++++++- 12 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/org/openedx/core/data/model/GatedContentModel.kt create mode 100644 core/src/main/java/org/openedx/core/data/model/SequenceModel.kt create mode 100644 core/src/main/java/org/openedx/core/domain/model/GatedContent.kt create mode 100644 core/src/main/java/org/openedx/core/domain/model/Subsection.kt diff --git a/core/src/main/java/org/openedx/core/data/api/CourseApi.kt b/core/src/main/java/org/openedx/core/data/api/CourseApi.kt index 8b5f0913a..3dd83b931 100644 --- a/core/src/main/java/org/openedx/core/data/api/CourseApi.kt +++ b/core/src/main/java/org/openedx/core/data/api/CourseApi.kt @@ -12,6 +12,7 @@ import org.openedx.core.data.model.CourseStructureModel import org.openedx.core.data.model.EnrollmentStatus import org.openedx.core.data.model.HandoutsModel import org.openedx.core.data.model.ResetCourseDates +import org.openedx.core.data.model.SequenceModel import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.Header @@ -100,4 +101,7 @@ interface CourseApi { suspend fun getEnrollmentDetails( @Path("course_id") courseId: String, ): CourseEnrollmentDetails + + @GET("api/courseware/sequence/{sequence_id}/") + suspend fun getSequence(@Path("sequence_id") sequenceId: String): SequenceModel } diff --git a/core/src/main/java/org/openedx/core/data/model/GatedContentModel.kt b/core/src/main/java/org/openedx/core/data/model/GatedContentModel.kt new file mode 100644 index 000000000..912f5145d --- /dev/null +++ b/core/src/main/java/org/openedx/core/data/model/GatedContentModel.kt @@ -0,0 +1,27 @@ +package org.openedx.core.data.model + +import com.google.gson.annotations.SerializedName +import org.openedx.core.domain.model.GatedContent + +data class GatedContentModel( + @SerializedName("prereq_id") + val prereqId: String?, + @SerializedName("prereq_url") + val prereqUrl: String?, + @SerializedName("prereq_section_name") + val prereqSectionName: String?, + @SerializedName("gated") + val gated: Boolean, + @SerializedName("gated_section_name") + val gatedSectionName: String?, +) { + fun mapToDomain(): GatedContent { + return GatedContent( + prereqId = prereqId, + prereqUrl = prereqUrl, + prereqSubsectionName = prereqSectionName, + gated = gated, + gatedSubsectionName = gatedSectionName + ) + } +} diff --git a/core/src/main/java/org/openedx/core/data/model/SequenceModel.kt b/core/src/main/java/org/openedx/core/data/model/SequenceModel.kt new file mode 100644 index 000000000..c88af3abc --- /dev/null +++ b/core/src/main/java/org/openedx/core/data/model/SequenceModel.kt @@ -0,0 +1,30 @@ +package org.openedx.core.data.model + +import com.google.gson.annotations.SerializedName +import org.openedx.core.domain.model.Subsection + +data class SequenceModel( + @SerializedName("element_id") + val elementId: String, + @SerializedName("item_id") + val itemId: String, + @SerializedName("banner_text") + val bannerText: String?, + @SerializedName("gated_content") + val gatedContentModel: GatedContentModel, + @SerializedName("sequence_name") + val sequenceName: String, + @SerializedName("display_name") + val displayName: String, +) { + fun mapToDomain(): Subsection { + return Subsection( + elementId = elementId, + itemId = itemId, + bannerText = bannerText, + subsectionName = sequenceName, + displayName = displayName, + gatedContent = gatedContentModel.mapToDomain(), + ) + } +} diff --git a/core/src/main/java/org/openedx/core/domain/model/GatedContent.kt b/core/src/main/java/org/openedx/core/domain/model/GatedContent.kt new file mode 100644 index 000000000..e0e2d758e --- /dev/null +++ b/core/src/main/java/org/openedx/core/domain/model/GatedContent.kt @@ -0,0 +1,13 @@ +package org.openedx.core.domain.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class GatedContent( + val prereqId: String?, + val prereqUrl: String?, + val prereqSubsectionName: String?, + val gated: Boolean, + val gatedSubsectionName: String? +) : Parcelable diff --git a/core/src/main/java/org/openedx/core/domain/model/Subsection.kt b/core/src/main/java/org/openedx/core/domain/model/Subsection.kt new file mode 100644 index 000000000..1371234b6 --- /dev/null +++ b/core/src/main/java/org/openedx/core/domain/model/Subsection.kt @@ -0,0 +1,14 @@ +package org.openedx.core.domain.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class Subsection( + val elementId: String, + val itemId: String, + val bannerText: String?, + val gatedContent: GatedContent, + val subsectionName: String, + val displayName: String +) : Parcelable diff --git a/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt b/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt index d9034e4ef..7daee2c1d 100644 --- a/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt +++ b/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt @@ -139,4 +139,6 @@ class CourseRepository( downloadDao.removeOfflineXBlockProgress(listOf(blockId)) } } + + suspend fun getSequence(sectionId: String) = api.getSequence(sectionId).mapToDomain() } diff --git a/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt b/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt index fdbcdd204..f158079d2 100644 --- a/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt +++ b/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt @@ -82,6 +82,8 @@ class CourseInteractor( suspend fun removeDownloadModel(id: String) = repository.removeDownloadModel(id) + suspend fun getSubsection(subsectionId: String) = repository.getSequence(subsectionId) + fun getDownloadModels() = repository.getDownloadModels() suspend fun getAllDownloadModels() = repository.getAllDownloadModels() diff --git a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt index 75a100ab8..b75e33ccf 100644 --- a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt +++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt @@ -4,6 +4,7 @@ import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup +import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -17,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.lazy.LazyColumn @@ -256,6 +258,34 @@ private fun CourseSectionScreen( } } + is CourseSectionUIState.Gated -> { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + painter = painterResource(id = R.drawable.ic_course_gated), + contentDescription = "gated", + modifier = Modifier.size(48.dp) + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + text = stringResource( + id = R.string.course_gated_subsection, + uiState.prereqSubsectionName ?: "" + ), + textAlign = TextAlign.Center, + style = MaterialTheme.appTypography.titleMedium, + color = MaterialTheme.appColors.textPrimary, + + ) + } + } + is CourseSectionUIState.Blocks -> { Column(Modifier.fillMaxSize()) { LazyColumn( diff --git a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt index 166da30c2..ae3e7b094 100644 --- a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt +++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt @@ -8,5 +8,9 @@ sealed class CourseSectionUIState { val sectionName: String, val courseName: String ) : CourseSectionUIState() + data class Gated( + val gatedSubsectionName: String?, + val prereqSubsectionName: String?, + ) : CourseSectionUIState() data object Loading : CourseSectionUIState() } diff --git a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt index 2ebe2c9b3..5f82d51f0 100644 --- a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt @@ -54,6 +54,14 @@ class CourseSectionViewModel( _uiState.value = CourseSectionUIState.Loading viewModelScope.launch { try { + val sectionData = interactor.getSubsection(blockId) + if (sectionData.gatedContent.gated) { + _uiState.value = CourseSectionUIState.Gated( + gatedSubsectionName = sectionData.gatedContent.gatedSubsectionName, + prereqSubsectionName = sectionData.gatedContent.prereqSubsectionName + ) + return@launch + } val courseStructure = when (mode) { CourseViewMode.FULL -> interactor.getCourseStructure(courseId) CourseViewMode.VIDEOS -> interactor.getCourseStructureForVideos(courseId) diff --git a/course/src/main/res/values/strings.xml b/course/src/main/res/values/strings.xml index 59c536295..32bae9aa6 100644 --- a/course/src/main/res/values/strings.xml +++ b/course/src/main/res/values/strings.xml @@ -34,6 +34,7 @@ Resume To proceed with \"%s\" press \"Next section\". Some content in this part of the course is locked for upgraded users only. + Content locked. You must complete the prerequisite: \“%s\” to access this content. You cannot change the download video quality when all videos are downloading Dates Shifted diff --git a/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt index 02eda9622..861e85e77 100644 --- a/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt +++ b/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt @@ -31,6 +31,8 @@ import org.openedx.core.domain.model.Block import org.openedx.core.domain.model.BlockCounts import org.openedx.core.domain.model.CourseStructure import org.openedx.core.domain.model.CoursewareAccess +import org.openedx.core.domain.model.GatedContent +import org.openedx.core.domain.model.Subsection import org.openedx.core.module.DownloadWorkerController import org.openedx.core.module.db.DownloadDao import org.openedx.core.module.db.DownloadModel @@ -161,6 +163,36 @@ class CourseSectionViewModelTest { progress = null ) + private val subsection = Subsection( + elementId = "id", + itemId = "id", + bannerText = "bannerText", + gatedContent = GatedContent( + prereqId = null, + prereqUrl = null, + prereqSubsectionName = null, + gated = false, + gatedSubsectionName = null + ), + subsectionName = "subsectionName", + displayName = "displayName" + ) + + private val gatedSubsection = Subsection( + elementId = "id", + itemId = "id", + bannerText = "bannerText", + gatedContent = GatedContent( + prereqId = "prereqId", + prereqUrl = "prereqUrl", + prereqSubsectionName = "prereqSubsectionName", + gated = true, + gatedSubsectionName = "gatedSubsectionName" + ), + subsectionName = "subsectionName", + displayName = "displayName" + ) + private val downloadModel = DownloadModel( "id", "title", @@ -181,6 +213,7 @@ class CourseSectionViewModelTest { every { resourceManager.getString(org.openedx.course.R.string.course_can_download_only_with_wifi) } returns cantDownload + coEvery { interactor.getSubsection("id") } returns subsection } @After @@ -199,13 +232,15 @@ class CourseSectionViewModelTest { analytics, ) + coEvery { interactor.getSubsection("") } throws UnknownHostException() coEvery { interactor.getCourseStructure(any()) } throws UnknownHostException() coEvery { interactor.getCourseStructureForVideos(any()) } throws UnknownHostException() viewModel.getBlocks("", CourseViewMode.FULL) advanceUntilIdle() - coVerify(exactly = 1) { interactor.getCourseStructure(any()) } + coVerify(exactly = 1) { interactor.getSubsection("") } + coVerify(exactly = 0) { interactor.getCourseStructure(any()) } coVerify(exactly = 0) { interactor.getCourseStructureForVideos(any()) } val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage @@ -224,13 +259,15 @@ class CourseSectionViewModelTest { analytics, ) + coEvery { interactor.getSubsection("id2") } throws Exception() coEvery { interactor.getCourseStructure(any()) } throws Exception() coEvery { interactor.getCourseStructureForVideos(any()) } throws Exception() viewModel.getBlocks("id2", CourseViewMode.FULL) advanceUntilIdle() - coVerify(exactly = 1) { interactor.getCourseStructure(any()) } + coVerify(exactly = 1) { interactor.getSubsection("id2") } + coVerify(exactly = 0) { interactor.getCourseStructure(any()) } coVerify(exactly = 0) { interactor.getCourseStructureForVideos(any()) } val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage @@ -341,4 +378,25 @@ class CourseSectionViewModelTest { assert(viewModel.uiState.value is CourseSectionUIState.Blocks) } + + @Test + fun `subsection is gated`() = runTest { + coEvery { downloadDao.getAllDataFlow() } returns flow { + emit(listOf(DownloadModelEntity.createFrom(downloadModel))) + } + val viewModel = CourseSectionViewModel( + "", + interactor, + resourceManager, + notifier, + analytics, + ) + + coEvery { interactor.getSubsection("id") } returns gatedSubsection + + viewModel.getBlocks("id", CourseViewMode.FULL) + advanceUntilIdle() + + assert(viewModel.uiState.value is CourseSectionUIState.Gated) + } } From 926b889396c99701c1d3f9741122320711424883 Mon Sep 17 00:00:00 2001 From: Demid Date: Thu, 24 Oct 2024 16:26:29 +0300 Subject: [PATCH 3/6] feat: "Go To Prerequisite Section" button and previews (cherry picked from commit 43e5679979f5a90c61b7acdaa03dcf1bf97753d9) (cherry picked from commit 442c393be46471c9afe44ba50dd07ce59393c861) --- .../data/repository/CourseRepository.kt | 2 +- .../course/presentation/CourseAnalytics.kt | 4 ++ .../section/CourseSectionFragment.kt | 44 ++++++++++++++++++- .../section/CourseSectionUIState.kt | 1 + .../section/CourseSectionViewModel.kt | 20 ++++++++- course/src/main/res/values/strings.xml | 1 + 6 files changed, 68 insertions(+), 4 deletions(-) diff --git a/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt b/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt index 7daee2c1d..0609ad583 100644 --- a/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt +++ b/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt @@ -140,5 +140,5 @@ class CourseRepository( } } - suspend fun getSequence(sectionId: String) = api.getSequence(sectionId).mapToDomain() + suspend fun getSequence(sequenceId: String) = api.getSequence(sequenceId).mapToDomain() } diff --git a/course/src/main/java/org/openedx/course/presentation/CourseAnalytics.kt b/course/src/main/java/org/openedx/course/presentation/CourseAnalytics.kt index 0dbe660e5..a5b01a097 100644 --- a/course/src/main/java/org/openedx/course/presentation/CourseAnalytics.kt +++ b/course/src/main/java/org/openedx/course/presentation/CourseAnalytics.kt @@ -78,6 +78,10 @@ enum class CourseAnalyticsEvent(val eventName: String, val biValue: String) { "Course:Unit Detail", "edx.bi.app.course.unit_detail" ), + PREREQUISITE( + "Course:Prerequisite", + "edx.bi.app.course.prerequisite" + ), VIEW_CERTIFICATE( "Course:View Certificate Clicked", "edx.bi.app.course.view_certificate.clicked" diff --git a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt index b75e33ccf..7b3b339ab 100644 --- a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt +++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt @@ -63,6 +63,7 @@ import org.openedx.core.domain.model.BlockCounts import org.openedx.core.presentation.course.CourseViewMode import org.openedx.core.ui.BackBtn import org.openedx.core.ui.HandleUIMessage +import org.openedx.core.ui.OpenEdXButton import org.openedx.core.ui.displayCutoutForLandscape import org.openedx.core.ui.statusBarsInset import org.openedx.core.ui.theme.OpenEdXTheme @@ -126,6 +127,15 @@ class CourseSectionFragment : Fragment() { ) } }, + onGoToPrerequisiteClick = { subSectionId -> + viewModel.goToPrerequisiteSectionClickedEvent(subSectionId) + router.navigateToCourseSubsections( + fm = requireActivity().supportFragmentManager, + courseId = viewModel.courseId, + subSectionId = subSectionId, + mode = CourseViewMode.FULL + ) + } ) LaunchedEffect(rememberSaveable { true }) { @@ -178,6 +188,7 @@ private fun CourseSectionScreen( uiMessage: UIMessage?, onBackClick: () -> Unit, onItemClick: (Block) -> Unit, + onGoToPrerequisiteClick: (String) -> Unit ) { val scaffoldState = rememberScaffoldState() val title = when (uiState) { @@ -281,7 +292,14 @@ private fun CourseSectionScreen( textAlign = TextAlign.Center, style = MaterialTheme.appTypography.titleMedium, color = MaterialTheme.appColors.textPrimary, - + ) + Spacer(modifier = Modifier.height(16.dp)) + OpenEdXButton( + text = stringResource(id = R.string.course_go_to_prerequisite_section), + onClick = { + onGoToPrerequisiteClick(uiState.prereqId ?: "") + }, + modifier = Modifier.padding(top = 16.dp) ) } } @@ -391,6 +409,7 @@ private fun CourseSectionScreenPreview() { uiMessage = null, onBackClick = {}, onItemClick = {}, + onGoToPrerequisiteClick = {} ) } } @@ -415,6 +434,29 @@ private fun CourseSectionScreenTabletPreview() { uiMessage = null, onBackClick = {}, onItemClick = {}, + onGoToPrerequisiteClick = {} + ) + } +} + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, device = Devices.NEXUS_9) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.NEXUS_9) +@Composable +private fun CourseSectionScreenGatedPreview() { + OpenEdXTheme { + CourseSectionScreen( + windowSize = WindowSize(WindowType.Medium, WindowType.Medium), + uiState = CourseSectionUIState.Gated( + "Gated Subsection", + "Prerequisite Subsection", + "Prerequisite Id" + ), + uiMessage = null, + onBackClick = {}, + onItemClick = {}, + onGoToPrerequisiteClick = {} ) } } diff --git a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt index ae3e7b094..484e144c9 100644 --- a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt +++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt @@ -11,6 +11,7 @@ sealed class CourseSectionUIState { data class Gated( val gatedSubsectionName: String?, val prereqSubsectionName: String?, + val prereqId: String?, ) : CourseSectionUIState() data object Loading : CourseSectionUIState() } diff --git a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt index 5f82d51f0..93adedc7b 100644 --- a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt @@ -57,9 +57,10 @@ class CourseSectionViewModel( val sectionData = interactor.getSubsection(blockId) if (sectionData.gatedContent.gated) { _uiState.value = CourseSectionUIState.Gated( + prereqId = sectionData.gatedContent.prereqId, + prereqSubsectionName = sectionData.gatedContent.prereqSubsectionName, gatedSubsectionName = sectionData.gatedContent.gatedSubsectionName, - prereqSubsectionName = sectionData.gatedContent.prereqSubsectionName - ) + ) return@launch } val courseStructure = when (mode) { @@ -126,4 +127,19 @@ class CourseSectionViewModel( ) } } + + fun goToPrerequisiteSectionClickedEvent(subSectionId: String) { + val currentState = uiState.value + if (currentState is CourseSectionUIState.Gated) { + analytics.logEvent( + event = CourseAnalyticsEvent.PREREQUISITE.eventName, + params = buildMap { + put(CourseAnalyticsKey.NAME.key, CourseAnalyticsEvent.PREREQUISITE.biValue) + put(CourseAnalyticsKey.COURSE_ID.key, courseId) + put(CourseAnalyticsKey.BLOCK_ID.key, subSectionId) + put(CourseAnalyticsKey.CATEGORY.key, CourseAnalyticsKey.NAVIGATION.key) + } + ) + } + } } diff --git a/course/src/main/res/values/strings.xml b/course/src/main/res/values/strings.xml index 32bae9aa6..5779f7eaa 100644 --- a/course/src/main/res/values/strings.xml +++ b/course/src/main/res/values/strings.xml @@ -35,6 +35,7 @@ To proceed with \"%s\" press \"Next section\". Some content in this part of the course is locked for upgraded users only. Content locked. You must complete the prerequisite: \“%s\” to access this content. + Go To Prerequisite Section You cannot change the download video quality when all videos are downloading Dates Shifted From 6b091b92429ed94dea8dffa4348587d80a521077 Mon Sep 17 00:00:00 2001 From: "kshitij.sobti" Date: Tue, 3 Dec 2024 16:06:40 +0530 Subject: [PATCH 4/6] fix: downloaded videos were not working due to gating API needing online access (cherry picked from commit f4ecc5e26df89a8a9f5480bfdeb5e82683bc9a94) (cherry picked from commit 9befe5f88348bd5f6d1ce36fdb736a264408c94b) --- .../java/org/openedx/app/di/ScreenModule.kt | 1 + .../section/CourseSectionViewModel.kt | 18 +++++++++++------- .../section/CourseSectionViewModelTest.kt | 7 +++++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/openedx/app/di/ScreenModule.kt b/app/src/main/java/org/openedx/app/di/ScreenModule.kt index 6b7692f99..abb238485 100644 --- a/app/src/main/java/org/openedx/app/di/ScreenModule.kt +++ b/app/src/main/java/org/openedx/app/di/ScreenModule.kt @@ -293,6 +293,7 @@ val screenModule = module { get(), get(), get(), + get(), ) } viewModel { (courseId: String, unitId: String) -> diff --git a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt index 93adedc7b..ebe8dc078 100644 --- a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt @@ -9,6 +9,7 @@ import org.openedx.core.BlockType import org.openedx.core.R import org.openedx.core.domain.model.Block import org.openedx.core.presentation.course.CourseViewMode +import org.openedx.core.system.connection.NetworkConnection import org.openedx.core.system.notifier.CourseNotifier import org.openedx.core.system.notifier.CourseSectionChanged import org.openedx.course.domain.interactor.CourseInteractor @@ -25,6 +26,7 @@ class CourseSectionViewModel( val courseId: String, private val interactor: CourseInteractor, private val resourceManager: ResourceManager, + private val networkConnection: NetworkConnection, private val notifier: CourseNotifier, private val analytics: CourseAnalytics, ) : BaseViewModel() { @@ -54,14 +56,16 @@ class CourseSectionViewModel( _uiState.value = CourseSectionUIState.Loading viewModelScope.launch { try { - val sectionData = interactor.getSubsection(blockId) - if (sectionData.gatedContent.gated) { - _uiState.value = CourseSectionUIState.Gated( - prereqId = sectionData.gatedContent.prereqId, - prereqSubsectionName = sectionData.gatedContent.prereqSubsectionName, - gatedSubsectionName = sectionData.gatedContent.gatedSubsectionName, + if (networkConnection.isOnline()) { + val sectionData = interactor.getSubsection(blockId) + if (sectionData.gatedContent.gated) { + _uiState.value = CourseSectionUIState.Gated( + prereqId = sectionData.gatedContent.prereqId, + prereqSubsectionName = sectionData.gatedContent.prereqSubsectionName, + gatedSubsectionName = sectionData.gatedContent.gatedSubsectionName, ) - return@launch + return@launch + } } val courseStructure = when (mode) { CourseViewMode.FULL -> interactor.getCourseStructure(courseId) diff --git a/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt index 861e85e77..5d23e322a 100644 --- a/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt +++ b/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt @@ -228,6 +228,7 @@ class CourseSectionViewModelTest { "", interactor, resourceManager, + networkConnection, notifier, analytics, ) @@ -255,6 +256,7 @@ class CourseSectionViewModelTest { "", interactor, resourceManager, + networkConnection, notifier, analytics, ) @@ -284,6 +286,7 @@ class CourseSectionViewModelTest { "", interactor, resourceManager, + networkConnection, notifier, analytics, ) @@ -313,6 +316,7 @@ class CourseSectionViewModelTest { "", interactor, resourceManager, + networkConnection, notifier, analytics, ) @@ -335,6 +339,7 @@ class CourseSectionViewModelTest { "", interactor, resourceManager, + networkConnection, notifier, analytics, ) @@ -360,6 +365,7 @@ class CourseSectionViewModelTest { "", interactor, resourceManager, + networkConnection, notifier, analytics, ) @@ -388,6 +394,7 @@ class CourseSectionViewModelTest { "", interactor, resourceManager, + networkConnection, notifier, analytics, ) From 1a20ad7b85a18167cda020d86235a43e94917dbe Mon Sep 17 00:00:00 2001 From: "kshitij.sobti" Date: Wed, 4 Dec 2024 17:51:16 +0530 Subject: [PATCH 5/6] fixup! fix: downloaded videos were not working due to gating API needing online access (cherry picked from commit 8f17fff8c9b926e4b1f7e36452e12027f0a7b2b0) (cherry picked from commit 885c26feeb64f8e5beb49c1801180883da3c29ab) --- .../course/presentation/section/CourseSectionViewModelTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt index 5d23e322a..f8e92cd4d 100644 --- a/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt +++ b/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt @@ -213,6 +213,7 @@ class CourseSectionViewModelTest { every { resourceManager.getString(org.openedx.course.R.string.course_can_download_only_with_wifi) } returns cantDownload + every { networkConnection.isOnline() } returns true coEvery { interactor.getSubsection("id") } returns subsection } From 4b5871acb80bc3f0bfdcd535b67bbce6b410c06c Mon Sep 17 00:00:00 2001 From: Volodymyr Chekyrta Date: Tue, 13 May 2025 12:55:11 +0300 Subject: [PATCH 6/6] fix: ProgressBarRangeInfo IllegalArgumentException - current must not be NaN (#443) (cherry picked from commit 6e59c7fe770bf7747081232bd5abbae468fc6539) --- .../org/openedx/core/domain/model/Progress.kt | 7 ++----- .../org/openedx/core/extension/FloatExt.kt | 19 +++++++++++++++++++ .../offline/CourseOfflineViewModel.kt | 14 ++++++++++---- .../course/presentation/ui/CourseUI.kt | 7 ++----- .../presentation/AllEnrolledCoursesView.kt | 7 +------ .../presentation/DashboardGalleryView.kt | 9 ++------- 6 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 core/src/main/java/org/openedx/core/extension/FloatExt.kt diff --git a/core/src/main/java/org/openedx/core/domain/model/Progress.kt b/core/src/main/java/org/openedx/core/domain/model/Progress.kt index 800a9c292..edbcf0f90 100644 --- a/core/src/main/java/org/openedx/core/domain/model/Progress.kt +++ b/core/src/main/java/org/openedx/core/domain/model/Progress.kt @@ -3,6 +3,7 @@ package org.openedx.core.domain.model import android.os.Parcelable import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize +import org.openedx.core.extension.safeDivBy @Parcelize data class Progress( @@ -11,11 +12,7 @@ data class Progress( ) : Parcelable { @IgnoredOnParcel - val value: Float = try { - assignmentsCompleted.toFloat() / totalAssignmentsCount.toFloat() - } catch (_: ArithmeticException) { - 0f - } + val value: Float = assignmentsCompleted.toFloat().safeDivBy(totalAssignmentsCount.toFloat()) companion object { val DEFAULT_PROGRESS = Progress(0, 0) diff --git a/core/src/main/java/org/openedx/core/extension/FloatExt.kt b/core/src/main/java/org/openedx/core/extension/FloatExt.kt new file mode 100644 index 000000000..77a022736 --- /dev/null +++ b/core/src/main/java/org/openedx/core/extension/FloatExt.kt @@ -0,0 +1,19 @@ +package org.openedx.core.extension + +/** + * Safely divides this Float by [divisor], returning 0f if: + * - [divisor] is zero, + * - the result is NaN. + * + * Workaround for accessibility issue: + * https://github.com/openedx/openedx-app-android/issues/442 + */ +fun Float.safeDivBy(divisor: Float): Float = try { + var result = this / divisor + if (result.isNaN()) { + result = 0f + } + result +} catch (_: ArithmeticException) { + 0f +} diff --git a/course/src/main/java/org/openedx/course/presentation/offline/CourseOfflineViewModel.kt b/course/src/main/java/org/openedx/course/presentation/offline/CourseOfflineViewModel.kt index 19d67f79b..5f6165566 100644 --- a/course/src/main/java/org/openedx/course/presentation/offline/CourseOfflineViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/offline/CourseOfflineViewModel.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.launch import org.openedx.core.BlockType import org.openedx.core.data.storage.CorePreferences import org.openedx.core.domain.model.Block +import org.openedx.core.extension.safeDivBy import org.openedx.core.module.DownloadWorkerController import org.openedx.core.module.db.DownloadDao import org.openedx.core.module.db.DownloadModel @@ -187,19 +188,24 @@ class CourseOfflineViewModel( completedDownloads: List, downloadedBlocks: List ) { - val downloadedSize = getFilesSize(downloadedBlocks) + val downloadedSize = getFilesSize(downloadedBlocks).toFloat() val realDownloadedSize = completedDownloads.sumOf { it.size } val largestDownloads = completedDownloads .sortedByDescending { it.size } .take(n = 5) - + val progressBarValue = downloadedSize.safeDivBy(totalDownloadableSize.toFloat()) + val readyToDownloadSize = if (progressBarValue >= 1) { + 0 + } else { + totalDownloadableSize - realDownloadedSize + } _uiState.update { it.copy( isHaveDownloadableBlocks = true, largestDownloads = largestDownloads, - readyToDownloadSize = (totalDownloadableSize - downloadedSize).toFileSize(1, false), + readyToDownloadSize = readyToDownloadSize.toFileSize(1, false), downloadedSize = realDownloadedSize.toFileSize(1, false), - progressBarValue = downloadedSize.toFloat() / totalDownloadableSize.toFloat() + progressBarValue = progressBarValue ) } } diff --git a/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt b/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt index 2598ad8ac..f0a87d8f4 100644 --- a/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt +++ b/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt @@ -84,6 +84,7 @@ import org.openedx.core.domain.model.AssignmentProgress import org.openedx.core.domain.model.Block import org.openedx.core.domain.model.BlockCounts import org.openedx.core.domain.model.CourseDatesBannerInfo +import org.openedx.core.extension.safeDivBy import org.openedx.core.module.db.DownloadModel import org.openedx.core.module.db.DownloadedState import org.openedx.core.module.db.FileType @@ -260,11 +261,7 @@ fun OfflineQueueCard( maxLines = 1 ) - val progress = if (progressSize == 0L) { - 0f - } else { - progressValue.toFloat() / progressSize - } + val progress = progressValue.toFloat().safeDivBy(progressSize.toFloat()) LinearProgressIndicator( modifier = Modifier .fillMaxWidth() diff --git a/dashboard/src/main/java/org/openedx/courses/presentation/AllEnrolledCoursesView.kt b/dashboard/src/main/java/org/openedx/courses/presentation/AllEnrolledCoursesView.kt index 9d26e39df..2ac69f18a 100644 --- a/dashboard/src/main/java/org/openedx/courses/presentation/AllEnrolledCoursesView.kt +++ b/dashboard/src/main/java/org/openedx/courses/presentation/AllEnrolledCoursesView.kt @@ -431,16 +431,11 @@ fun CourseItem( .fillMaxWidth() .height(90.dp) ) - val progress: Float = try { - course.progress.assignmentsCompleted.toFloat() / course.progress.totalAssignmentsCount.toFloat() - } catch (_: ArithmeticException) { - 0f - } LinearProgressIndicator( modifier = Modifier .fillMaxWidth() .height(8.dp), - progress = progress, + progress = course.progress.value, color = MaterialTheme.appColors.primary, backgroundColor = MaterialTheme.appColors.divider ) diff --git a/dashboard/src/main/java/org/openedx/courses/presentation/DashboardGalleryView.kt b/dashboard/src/main/java/org/openedx/courses/presentation/DashboardGalleryView.kt index 2c44c2c61..dd50d1f3b 100644 --- a/dashboard/src/main/java/org/openedx/courses/presentation/DashboardGalleryView.kt +++ b/dashboard/src/main/java/org/openedx/courses/presentation/DashboardGalleryView.kt @@ -557,17 +557,12 @@ private fun PrimaryCourseCard( .fillMaxWidth() .height(140.dp) ) - val progress: Float = try { - primaryCourse.progress.assignmentsCompleted.toFloat() / - primaryCourse.progress.totalAssignmentsCount.toFloat() - } catch (_: ArithmeticException) { - 0f - } + LinearProgressIndicator( modifier = Modifier .fillMaxWidth() .height(8.dp), - progress = progress, + progress = primaryCourse.progress.value, color = MaterialTheme.appColors.primary, backgroundColor = MaterialTheme.appColors.divider )