diff --git a/app/src/main/java/org/openedx/app/AppRouter.kt b/app/src/main/java/org/openedx/app/AppRouter.kt
index 90f6625e8..a2957848c 100644
--- a/app/src/main/java/org/openedx/app/AppRouter.kt
+++ b/app/src/main/java/org/openedx/app/AppRouter.kt
@@ -320,6 +320,13 @@ class AppRouter :
HandoutsWebViewFragment.newInstance(type.name, courseId)
)
}
+
+ override fun navigateToCertificate(fm: FragmentManager, title: String, url: String) {
+ replaceFragmentWithBackStack(
+ fm,
+ WebContentFragment.newInstance(title = title, url = url)
+ )
+ }
// endregion
// region DiscussionRouter
diff --git a/core/src/main/java/org/openedx/core/extension/WebViewExt.kt b/core/src/main/java/org/openedx/core/extension/WebViewExt.kt
new file mode 100644
index 000000000..ca47b1eaf
--- /dev/null
+++ b/core/src/main/java/org/openedx/core/extension/WebViewExt.kt
@@ -0,0 +1,51 @@
+package org.openedx.core.extension
+
+import android.webkit.WebView
+
+/**
+ * Injects CSS to hide common headers and footers in web pages.
+ *
+ * Also removes padding/margins from body to eliminate empty space.
+ */
+fun WebView.injectHeaderFooterHidingCss() {
+ val css = """
+ /* Hide common site headers/footers */
+ header, footer,
+ [role="banner"], [role="contentinfo"],
+ .site-header, .site-footer,
+ .global-header, .global-footer,
+ .header, .footer,
+ .navbar-fixed-top, .navbar, .topbar,
+ .bottom-bar, .cookie-banner, .gdpr-banner,
+ .certificate .wrapper-banner.wrapper-banner-user,
+ #header, #footer, #masthead, #site-footer, #site-header {
+ display: none !important;
+ visibility: hidden !important;
+ height: 0 !important;
+ min-height: 0 !important;
+ max-height: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ border: 0 !important;
+ }
+
+ /* Remove empty space created by fixed headers */
+ body {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ }
+ """
+
+ val js = """
+ (function() {
+ var style = document.createElement('style');
+ style.type = 'text/css';
+ style.appendChild(document.createTextNode(`$css`));
+ document.head.appendChild(style);
+ })();
+ """.trimIndent()
+
+ evaluateJavascript(js, null)
+}
diff --git a/core/src/main/java/org/openedx/core/ui/WebContentScreen.kt b/core/src/main/java/org/openedx/core/ui/WebContentScreen.kt
index 296f9999e..48657ede0 100644
--- a/core/src/main/java/org/openedx/core/ui/WebContentScreen.kt
+++ b/core/src/main/java/org/openedx/core/ui/WebContentScreen.kt
@@ -34,6 +34,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex
+import org.openedx.core.extension.injectHeaderFooterHidingCss
import org.openedx.core.ui.theme.appColors
import org.openedx.core.ui.theme.getDarkThemeFromPreferences
import org.openedx.core.utils.EmailUtil
@@ -53,6 +54,7 @@ fun WebContentScreen(
onBackClick: () -> Unit,
htmlBody: String? = null,
contentUrl: String? = null,
+ hideHeaderFooter: Boolean = true,
) {
val scaffoldState = rememberScaffoldState()
Scaffold(
@@ -111,6 +113,7 @@ fun WebContentScreen(
apiHostUrl = apiHostUrl,
body = htmlBody,
contentUrl = contentUrl,
+ hideHeaderFooter = hideHeaderFooter,
onWebPageLoaded = {
webViewAlpha = 1f
}
@@ -129,6 +132,7 @@ private fun WebViewContent(
apiHostUrl: String? = null,
body: String? = null,
contentUrl: String? = null,
+ hideHeaderFooter: Boolean = true,
onWebPageLoaded: () -> Unit
) {
val context = LocalContext.current
@@ -165,11 +169,12 @@ private fun WebViewContent(
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
- val css = when {
- url?.contains("privacy", ignoreCase = true) == true -> PRIVACY_PAGE_CSS
- else -> return
+ if (hideHeaderFooter) {
+ if (url?.contains("privacy", ignoreCase = true) == true) {
+ injectCss(view, PRIVACY_PAGE_ADDITIONAL_CSS)
+ }
+ view?.injectHeaderFooterHidingCss()
}
- injectCss(view, css)
}
}
with(settings) {
@@ -214,14 +219,7 @@ private fun WebViewContent(
)
}
-private const val PRIVACY_PAGE_CSS = """
- header, footer {
- display: none !important;
- }
- body {
- margin-top: 0 !important;
- padding-top: 0 !important;
- }
+private const val PRIVACY_PAGE_ADDITIONAL_CSS = """
.content-wrapper {
padding: 0 !important;
margin: 0;
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 4702b8198..008ae9caf 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -200,4 +200,7 @@
Language
+
+
+ Certificate
diff --git a/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt b/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt
index 1f874e055..2d833787f 100644
--- a/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt
+++ b/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt
@@ -66,4 +66,6 @@ interface CourseRouter {
fun navigateToVideoQuality(fm: FragmentManager, videoQualityType: VideoQualityType)
fun navigateToDiscover(fm: FragmentManager)
+
+ fun navigateToCertificate(fm: FragmentManager, title: String, url: String)
}
diff --git a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineScreen.kt b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineScreen.kt
index 27c4594da..b02435cae 100644
--- a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineScreen.kt
+++ b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineScreen.kt
@@ -33,8 +33,6 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.platform.AndroidUriHandler
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
@@ -87,7 +85,6 @@ fun CourseOutlineScreen(
val uiState by viewModel.uiState.collectAsState()
val uiMessage by viewModel.uiMessage.collectAsState(null)
val resumeBlockId by viewModel.resumeBlockId.collectAsState("")
- val context = LocalContext.current
LaunchedEffect(resumeBlockId) {
if (resumeBlockId.isNotEmpty()) {
@@ -156,7 +153,7 @@ fun CourseOutlineScreen(
onCertificateClick = {
viewModel.viewCertificateTappedEvent()
it.takeIfNotEmpty()
- ?.let { url -> AndroidUriHandler(context).openUri(url) }
+ ?.let { url -> viewModel.navigateToCertificate(fragmentManager, url) }
}
)
}
diff --git a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt
index f28013272..44964e964 100644
--- a/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt
+++ b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt
@@ -376,6 +376,15 @@ class CourseOutlineViewModel(
)
}
+ fun navigateToCertificate(fm: FragmentManager, url: String) {
+ viewCertificateTappedEvent()
+ courseRouter.navigateToCertificate(
+ fm = fm,
+ title = resourceManager.getString(org.openedx.core.R.string.core_certificate),
+ url = url
+ )
+ }
+
private fun resumeCourseTappedEvent(blockId: String) {
val currentState = uiState.value
if (currentState is CourseOutlineUIState.CourseData) {