Skip to content

Commit 45cb062

Browse files
committed
Merge remote-tracking branch 'origin/develop' into hamza/LEARNER-10192
# Conflicts: # course/src/test/java/org/openedx/course/presentation/container/CourseContainerViewModelTest.kt
2 parents f2ea984 + b892560 commit 45cb062

File tree

24 files changed

+135
-163
lines changed

24 files changed

+135
-163
lines changed

app/src/main/java/org/openedx/app/di/AppModule.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,14 @@ val appModule = module {
180180
DownloadWorkerController(get(), get(), get())
181181
}
182182

183-
single { AppData(versionName = BuildConfig.VERSION_NAME) }
183+
single {
184+
val resourceManager = get<ResourceManager>()
185+
AppData(
186+
appName = resourceManager.getString(R.string.app_name),
187+
versionName = BuildConfig.VERSION_NAME,
188+
applicationId = BuildConfig.APPLICATION_ID,
189+
)
190+
}
184191
factory { (activity: AppCompatActivity) -> AppReviewManager(activity, get(), get()) }
185192

186193
single { TranscriptManager(get(), get()) }

app/src/main/java/org/openedx/app/di/ScreenModule.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ val screenModule = module {
172172
get(),
173173
get(),
174174
get(),
175+
get(),
175176
)
176177
}
177178

@@ -229,6 +230,7 @@ val screenModule = module {
229230
get(),
230231
get(),
231232
get(),
233+
get(),
232234
)
233235
}
234236
viewModel { (courseId: String) ->
@@ -460,7 +462,7 @@ val screenModule = module {
460462
)
461463
}
462464

463-
viewModel { ProgramViewModel(get(), get(), get(), get(), get(), get(), get()) }
465+
viewModel { ProgramViewModel(get(), get(), get(), get(), get(), get(), get(), get()) }
464466

465467
viewModel { (courseId: String, courseTitle: String) ->
466468
CourseOfflineViewModel(

core/src/main/java/org/openedx/core/module/TranscriptManager.kt

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import okhttp3.OkHttpClient
55
import org.openedx.core.module.download.AbstractDownloader
66
import org.openedx.core.utils.Directories
77
import org.openedx.core.utils.IOUtils
8+
import org.openedx.core.utils.Logger
89
import org.openedx.core.utils.Sha1Util
910
import org.openedx.foundation.utils.FileUtil
1011
import subtitleFile.FormatSRT
@@ -21,6 +22,8 @@ class TranscriptManager(
2122
val fileUtil: FileUtil
2223
) {
2324

25+
private val logger = Logger(TAG)
26+
2427
private val transcriptDownloader = object : AbstractDownloader() {
2528
override val client: OkHttpClient
2629
get() = OkHttpClient.Builder().build()
@@ -62,17 +65,18 @@ class TranscriptManager(
6265
}
6366

6467
private suspend fun startTranscriptDownload(downloadLink: String) {
65-
if (!has(downloadLink)) {
66-
val file = File(getTranscriptDir(), Sha1Util.SHA1(downloadLink))
67-
val result = transcriptDownloader.download(
68-
downloadLink,
69-
file.path
70-
)
71-
if (result == AbstractDownloader.DownloadResult.SUCCESS) {
72-
getInputStream(downloadLink)?.let {
73-
val transcriptTimedTextObject =
74-
convertIntoTimedTextObject(it)
75-
transcriptObject = transcriptTimedTextObject
68+
if (has(downloadLink)) return
69+
val file = File(getTranscriptDir(), Sha1Util.SHA1(downloadLink))
70+
val result = transcriptDownloader.download(
71+
downloadLink,
72+
file.path
73+
)
74+
if (result == AbstractDownloader.DownloadResult.SUCCESS) {
75+
getInputStream(downloadLink)?.let {
76+
try {
77+
transcriptObject = convertIntoTimedTextObject(it)
78+
} catch (e: NullPointerException) {
79+
logger.e(throwable = e, submitCrashReport = true)
7680
}
7781
}
7882
}
@@ -86,7 +90,7 @@ class TranscriptManager(
8690
try {
8791
transcriptObject = convertIntoTimedTextObject(transcriptInputStream)
8892
} catch (e: Exception) {
89-
e.printStackTrace()
93+
logger.e(throwable = e, submitCrashReport = true)
9094
}
9195
} else {
9296
startTranscriptDownload(transcriptUrl)
@@ -127,6 +131,7 @@ class TranscriptManager(
127131
}
128132

129133
companion object {
134+
private const val TAG = "TranscriptManager"
130135
private const val FILE_VALIDITY_DURATION_HOURS = 5L
131136
}
132137
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package org.openedx.core.presentation.global
22

33
data class AppData(
4+
val appName: String,
5+
val applicationId: String,
46
val versionName: String,
5-
)
7+
) {
8+
val appUserAgent get() = "$appName/$applicationId/$versionName"
9+
}

core/src/main/java/org/openedx/core/ui/ComposeCommon.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1215,7 +1215,7 @@ fun FullScreenErrorView(
12151215
modifier = Modifier
12161216
.widthIn(Dp.Unspecified, 162.dp),
12171217
text = stringResource(id = errorType.actionResId),
1218-
textColor = MaterialTheme.appColors.primaryButtonText,
1218+
textColor = MaterialTheme.appColors.secondaryButtonText,
12191219
backgroundColor = MaterialTheme.appColors.secondaryButtonBackground,
12201220
onClick = onReloadClick,
12211221
)

core/src/main/java/org/openedx/core/utils/Logger.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
package org.openedx.core.utils
22

33
import android.util.Log
4+
import com.google.firebase.crashlytics.FirebaseCrashlytics
5+
import org.koin.core.component.KoinComponent
6+
import org.koin.core.component.inject
47
import org.openedx.core.BuildConfig
8+
import org.openedx.core.config.Config
9+
10+
class Logger(private val tag: String) : KoinComponent {
11+
12+
private val config by inject<Config>()
513

6-
class Logger(private val tag: String) {
714
fun d(message: () -> String) {
815
if (BuildConfig.DEBUG) Log.d(tag, message())
916
}
@@ -12,6 +19,13 @@ class Logger(private val tag: String) {
1219
if (BuildConfig.DEBUG) Log.e(tag, message())
1320
}
1421

22+
fun e(throwable: Throwable, submitCrashReport: Boolean = false) {
23+
if (BuildConfig.DEBUG) throwable.printStackTrace()
24+
if (submitCrashReport && config.getFirebaseConfig().enabled) {
25+
FirebaseCrashlytics.getInstance().recordException(throwable)
26+
}
27+
}
28+
1529
fun i(message: () -> String) {
1630
if (BuildConfig.DEBUG) Log.i(tag, message())
1731
}

course/src/main/java/org/openedx/course/presentation/container/CourseContainerFragment.kt

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,10 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
5656
import androidx.compose.ui.res.painterResource
5757
import androidx.compose.ui.res.stringResource
5858
import androidx.compose.ui.text.style.TextAlign
59-
import androidx.compose.ui.tooling.preview.Preview
6059
import androidx.compose.ui.unit.dp
6160
import androidx.core.os.bundleOf
6261
import androidx.core.view.isVisible
6362
import androidx.fragment.app.Fragment
64-
import androidx.fragment.app.FragmentActivity
6563
import androidx.fragment.app.FragmentManager
6664
import androidx.lifecycle.lifecycleScope
6765
import com.google.android.material.snackbar.Snackbar
@@ -164,10 +162,7 @@ class CourseContainerFragment : Fragment(R.layout.fragment_course_container) {
164162
}
165163
}
166164
viewModel.errorMessage.observe(viewLifecycleOwner) {
167-
snackBar = Snackbar.make(binding.root, it, Snackbar.LENGTH_INDEFINITE)
168-
.setAction(org.openedx.core.R.string.core_error_try_again) {
169-
viewModel.fetchCourseDetails()
170-
}
165+
snackBar = Snackbar.make(binding.root, it, Snackbar.LENGTH_SHORT)
171166
snackBar?.show()
172167
}
173168
viewLifecycleOwner.lifecycleScope.launch {
@@ -180,6 +175,8 @@ class CourseContainerFragment : Fragment(R.layout.fragment_course_container) {
180175
private fun onRefresh(currentPage: Int) {
181176
if (viewModel.courseAccessStatus.value == CourseAccessError.NONE) {
182177
viewModel.onRefresh(CourseContainerTab.entries[currentPage])
178+
} else {
179+
viewModel.fetchCourseDetails()
183180
}
184181
}
185182

@@ -390,7 +387,7 @@ fun CourseDashboard(
390387
isInternetConnectionShown = true
391388
},
392389
onReloadClick = {
393-
isInternetConnectionShown = true
390+
isInternetConnectionShown = viewModel.hasInternetConnection
394391
onRefresh(pagerState.currentPage)
395392
}
396393
)
@@ -520,7 +517,7 @@ private fun DashboardPager(
520517

521518
@Composable
522519
private fun CourseAccessErrorView(
523-
viewModel: CourseContainerViewModel?,
520+
viewModel: CourseContainerViewModel,
524521
accessError: CourseAccessError?,
525522
fragmentManager: FragmentManager,
526523
) {
@@ -532,7 +529,7 @@ private fun CourseAccessErrorView(
532529
R.string.course_error_expired_not_upgradeable_title,
533530
TimeUtils.getCourseAccessFormattedDate(
534531
LocalContext.current,
535-
viewModel?.courseDetails?.courseAccessDetails?.auditAccessExpires ?: Date()
532+
viewModel.courseDetails?.courseAccessDetails?.auditAccessExpires ?: Date()
536533
)
537534
)
538535
}
@@ -541,7 +538,7 @@ private fun CourseAccessErrorView(
541538
icon = painterResource(id = R.drawable.course_ic_calendar)
542539
message = stringResource(
543540
R.string.course_error_not_started_title,
544-
viewModel?.courseDetails?.courseInfoOverview?.startDisplay ?: ""
541+
viewModel.courseDetails?.courseInfoOverview?.startDisplay ?: ""
545542
)
546543
}
547544

@@ -595,6 +592,7 @@ private fun CourseAccessErrorView(
595592
)
596593
}
597594
SetupCourseAccessErrorButtons(
595+
viewModel = viewModel,
598596
accessError = accessError,
599597
fragmentManager = fragmentManager,
600598
)
@@ -604,20 +602,29 @@ private fun CourseAccessErrorView(
604602

605603
@Composable
606604
private fun SetupCourseAccessErrorButtons(
605+
viewModel: CourseContainerViewModel,
607606
accessError: CourseAccessError?,
608607
fragmentManager: FragmentManager,
609608
) {
610609
when (accessError) {
611610
CourseAccessError.AUDIT_EXPIRED_NOT_UPGRADABLE,
612611
CourseAccessError.NOT_YET_STARTED,
613-
CourseAccessError.UNKNOWN,
614612
-> {
615613
OpenEdXButton(
616614
text = stringResource(R.string.course_label_back),
617615
onClick = { fragmentManager.popBackStack() },
618616
)
619617
}
620618

619+
CourseAccessError.UNKNOWN -> {
620+
if (viewModel.hasInternetConnection) {
621+
OpenEdXButton(
622+
text = stringResource(R.string.course_label_back),
623+
onClick = { fragmentManager.popBackStack() },
624+
)
625+
}
626+
}
627+
621628
else -> {}
622629
}
623630
}
@@ -628,17 +635,3 @@ private fun scrollToDates(scope: CoroutineScope, pagerState: PagerState) {
628635
pagerState.animateScrollToPage(CourseContainerTab.entries.indexOf(CourseContainerTab.DATES))
629636
}
630637
}
631-
632-
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
633-
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
634-
@Composable
635-
private fun CourseAccessErrorViewPreview() {
636-
val context = LocalContext.current
637-
OpenEdXTheme {
638-
CourseAccessErrorView(
639-
viewModel = null,
640-
accessError = CourseAccessError.AUDIT_EXPIRED_NOT_UPGRADABLE,
641-
fragmentManager = (context as? FragmentActivity)?.supportFragmentManager!!
642-
)
643-
}
644-
}

course/src/main/java/org/openedx/course/presentation/container/CourseContainerViewModel.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ class CourseContainerViewModel(
202202
delay(500L)
203203
courseNotifier.send(CourseOpenBlock(resumeBlockId))
204204
}
205+
_dataReady.value = true
205206
}
206207
} ?: run {
207208
_courseAccessStatus.value = CourseAccessError.UNKNOWN
@@ -276,14 +277,9 @@ class CourseContainerViewModel(
276277
viewModelScope.launch {
277278
try {
278279
interactor.getCourseStructure(courseId, isNeedRefresh = true)
279-
} catch (e: Exception) {
280-
if (e.isInternetError()) {
281-
_errorMessage.value =
282-
resourceManager.getString(CoreR.string.core_error_no_connection)
283-
} else {
284-
_errorMessage.value =
285-
resourceManager.getString(CoreR.string.core_error_unknown_error)
286-
}
280+
} catch (ignore: Exception) {
281+
_errorMessage.value =
282+
resourceManager.getString(CoreR.string.core_error_unknown_error)
287283
}
288284
_refreshing.value = false
289285
courseNotifier.send(CourseStructureUpdated(courseId))

course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -884,15 +884,15 @@ fun SubSectionUnitsTitle(
884884
onUnitsClick: () -> Unit,
885885
) {
886886
val textStyle = MaterialTheme.appTypography.titleMedium
887-
val hasUnits = unitsCount > 0
887+
val hasMultipleUnits = unitsCount > 1
888888
var rowModifier = Modifier
889889
.fillMaxWidth()
890890
.padding(
891891
horizontal = 16.dp,
892892
vertical = 8.dp
893893
)
894894
.displayCutoutForLandscape()
895-
if (hasUnits) {
895+
if (hasMultipleUnits) {
896896
rowModifier = rowModifier.noRippleClickable { onUnitsClick() }
897897
}
898898

@@ -912,7 +912,7 @@ fun SubSectionUnitsTitle(
912912
textAlign = TextAlign.Start
913913
)
914914

915-
if (hasUnits) {
915+
if (hasMultipleUnits) {
916916
Icon(
917917
modifier = Modifier.rotate(if (unitsListShowed) 180f else 0f),
918918
painter = painterResource(id = R.drawable.ic_course_arrow_down),

course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerFragment.kt

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -281,21 +281,24 @@ class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_conta
281281

282282
binding.subSectionUnitsList.setContent {
283283
val unitBlocks by viewModel.subSectionUnitBlocks.collectAsState()
284-
val selectedUnitIndex = unitBlocks.indexOfFirst { it.id == viewModel.unitId }
285-
OpenEdXTheme {
286-
SubSectionUnitsList(
287-
unitBlocks = unitBlocks,
288-
selectedUnitIndex = selectedUnitIndex
289-
) { index, unit ->
290-
if (index != selectedUnitIndex) {
291-
router.navigateToCourseContainer(
292-
fm = requireActivity().supportFragmentManager,
293-
courseId = viewModel.courseId,
294-
unitId = unit.id,
295-
mode = requireArguments().serializable(ARG_MODE)!!
296-
)
297-
} else {
298-
handleUnitsClick()
284+
// If there is more than one unit in the section, show the list
285+
if (unitBlocks.size > 1) {
286+
val selectedUnitIndex = unitBlocks.indexOfFirst { it.id == viewModel.unitId }
287+
OpenEdXTheme {
288+
SubSectionUnitsList(
289+
unitBlocks = unitBlocks,
290+
selectedUnitIndex = selectedUnitIndex
291+
) { index, unit ->
292+
if (index != selectedUnitIndex) {
293+
router.navigateToCourseContainer(
294+
fm = requireActivity().supportFragmentManager,
295+
courseId = viewModel.courseId,
296+
unitId = unit.id,
297+
mode = requireArguments().serializable(ARG_MODE)!!
298+
)
299+
} else {
300+
handleUnitsClick()
301+
}
299302
}
300303
}
301304
}

0 commit comments

Comments
 (0)