Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/src/main/java/org/openedx/core/extension/StringExt.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.openedx.core.extension

import android.util.Patterns
import java.net.URL
import java.util.Locale
import java.util.regex.Pattern

Expand Down Expand Up @@ -38,6 +39,14 @@ fun String.takeIfNotEmpty(): String? {
return if (this.isEmpty().not()) this else null
}

fun String?.equalsHost(host: String?): Boolean {
return try {
host?.startsWith(URL(this).host, ignoreCase = true) == true
} catch (e: Exception) {
false
}
}

fun String.toImageLink(apiHostURL: String): String =
if (this.isLinkValid()) {
this
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.openedx.core.presentation.global

import org.openedx.core.R

enum class ErrorType(
val iconResId: Int = 0,
val titleResId: Int = 0,
val descriptionResId: Int = 0,
val actionResId: Int = 0,
) {
CONNECTION_ERROR(
iconResId = R.drawable.core_no_internet_connection,
titleResId = R.string.core_no_internet_connection,
descriptionResId = R.string.core_no_internet_connection_description,
actionResId = R.string.core_reload,
),
UNKNOWN_ERROR(
iconResId = R.drawable.core_ic_unknown_error,
titleResId = R.string.core_try_again,
descriptionResId = R.string.core_something_went_wrong_description,
actionResId = R.string.core_reload,
),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.openedx.core.presentation.global.webview

import org.openedx.core.presentation.global.ErrorType

sealed class WebViewUIState {
data object Loading : WebViewUIState()
data object Loaded : WebViewUIState()
data class Error(val errorType: ErrorType) : WebViewUIState()
}

enum class WebViewUIAction {
WEB_PAGE_LOADED,
WEB_PAGE_ERROR,
RELOAD_WEB_PAGE
}
31 changes: 18 additions & 13 deletions core/src/main/java/org/openedx/core/ui/ComposeCommon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ import org.openedx.core.domain.model.RegistrationField
import org.openedx.core.extension.LinkedImageText
import org.openedx.core.extension.tagId
import org.openedx.core.extension.toastMessage
import org.openedx.core.presentation.global.ErrorType
import org.openedx.core.ui.theme.OpenEdXTheme
import org.openedx.core.ui.theme.appColors
import org.openedx.core.ui.theme.appShapes
Expand Down Expand Up @@ -1136,33 +1137,41 @@ fun BackBtn(
}

@Composable
fun ConnectionErrorView(
modifier: Modifier,
onReloadClick: () -> Unit,
fun ConnectionErrorView(onReloadClick: () -> Unit) {
FullScreenErrorView(errorType = ErrorType.CONNECTION_ERROR, onReloadClick = onReloadClick)
}

@Composable
fun FullScreenErrorView(
modifier: Modifier = Modifier,
errorType: ErrorType,
onReloadClick: () -> Unit
) {
Column(
modifier = modifier,
modifier = modifier
.fillMaxSize()
.background(MaterialTheme.appColors.background),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
modifier = Modifier.size(100.dp),
painter = painterResource(id = R.drawable.core_no_internet_connection),
painter = painterResource(id = errorType.iconResId),
contentDescription = null,
tint = MaterialTheme.appColors.onSurface
)
Spacer(Modifier.height(28.dp))
Text(
modifier = Modifier.fillMaxWidth(0.8f),
text = stringResource(id = R.string.core_no_internet_connection),
text = stringResource(id = errorType.titleResId),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.titleLarge,
textAlign = TextAlign.Center
)
Spacer(Modifier.height(16.dp))
Text(
modifier = Modifier.fillMaxWidth(0.8f),
text = stringResource(id = R.string.core_no_internet_connection_description),
text = stringResource(id = errorType.descriptionResId),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.bodyLarge,
textAlign = TextAlign.Center
Expand All @@ -1171,7 +1180,7 @@ fun ConnectionErrorView(
OpenEdXButton(
modifier = Modifier
.widthIn(Dp.Unspecified, 162.dp),
text = stringResource(id = R.string.core_reload),
text = stringResource(id = errorType.actionResId),
textColor = MaterialTheme.appColors.primaryButtonText,
backgroundColor = MaterialTheme.appColors.secondaryButtonBackground,
onClick = onReloadClick,
Expand Down Expand Up @@ -1372,11 +1381,7 @@ private fun IconTextPreview() {
@Composable
private fun ConnectionErrorViewPreview() {
OpenEdXTheme(darkTheme = true) {
ConnectionErrorView(
modifier = Modifier
.fillMaxSize(),
onReloadClick = {}
)
ConnectionErrorView(onReloadClick = {})
}
}

Expand Down
23 changes: 23 additions & 0 deletions core/src/main/res/drawable/core_ic_unknown_error.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="86dp"
android:height="86dp"
android:viewportWidth="100"
android:viewportHeight="100">

<path
android:fillColor="#FFFFFF"
android:pathData="M85,61.35V20.67C85,18.09 82.91,16 80.33,16H19.67C17.09,16 15,18.09 15,20.67V61.35C15,62 15.52,62.52 16.17,62.52H83.83C84.48,62.52 85,62 85,61.35ZM55.74,34.47C55.28,34.01 55.29,33.25 55.77,32.8C56.23,32.36 56.97,32.4 57.43,32.86L58.44,33.87C58.67,34.1 59.04,34.1 59.26,33.87L60.28,32.86C60.73,32.4 61.47,32.36 61.93,32.8C62.42,33.25 62.42,34.01 61.96,34.47L60.91,35.52C60.69,35.75 60.69,36.11 60.91,36.34L61.93,37.36C62.68,38.11 62.19,39.38 61.14,39.38C60.84,39.38 60.54,39.27 60.31,39.04L59.26,37.99C59.04,37.76 58.67,37.76 58.44,37.99L57.39,39.04C56.92,39.52 56.13,39.49 55.68,38.98C55.28,38.51 55.33,37.8 55.77,37.36L56.79,36.34C57.02,36.11 57.02,35.75 56.79,35.52L55.74,34.47ZM59.67,50.9C60.41,51.63 59.89,52.9 58.85,52.9C58.56,52.9 58.26,52.78 58.03,52.56C54.93,49.52 51.1,49.28 50,49.28C45.65,49.28 42.75,51.79 41.97,52.56C41.51,53.01 40.77,53.01 40.32,52.55C39.86,52.09 39.87,51.35 40.33,50.9C45.81,45.52 54.43,45.75 59.67,50.9ZM38.04,34.47C37.58,34.01 37.58,33.25 38.07,32.8C38.53,32.36 39.27,32.4 39.72,32.86L40.74,33.87C40.96,34.1 41.33,34.1 41.56,33.87L42.57,32.86C43.02,32.4 43.77,32.36 44.23,32.8C44.71,33.25 44.72,34.01 44.26,34.47L43.21,35.52C42.98,35.75 42.98,36.11 43.21,36.34L44.23,37.36C44.98,38.11 44.49,39.38 43.43,39.38C43.13,39.38 42.84,39.27 42.61,39.04L41.56,37.99C41.33,37.76 40.96,37.76 40.74,37.99L39.69,39.04C39.21,39.52 38.43,39.49 37.98,38.98C37.58,38.51 37.63,37.8 38.07,37.36L39.09,36.34C39.31,36.11 39.31,35.75 39.09,35.52L38.04,34.47Z" />

<path
android:fillColor="#FFFFFF"
android:pathData="M15,66.02V67.48C15,70.06 17.09,72.15 19.67,72.15H80.33C82.88,72.15 84.96,70.09 85,67.55V66.02C85,65.38 84.48,64.85 83.83,64.85H16.17C15.52,64.85 15,65.38 15,66.02Z" />

<path
android:fillColor="#FFFFFF"
android:pathData="M32.55,83.28C32.36,82.67 32.71,82.01 33.32,81.82C43.55,78.64 56.47,78.65 66.68,81.82C67.3,82.01 67.64,82.67 67.45,83.28C67.26,83.9 66.6,84.24 65.99,84.05C56.28,81.03 43.72,81.03 34.01,84.05C33.4,84.24 32.74,83.9 32.55,83.28Z" />

<path
android:fillColor="#FFFFFF"
android:pathData="M56.92,77.47C52.43,76.98 47.67,76.97 43.08,77.47C42.26,77.56 41.61,76.79 41.83,75.99L42.02,75.33C42.16,74.83 42.62,74.48 43.14,74.48H56.86C57.38,74.48 57.84,74.83 57.98,75.33L58.17,75.99C58.39,76.79 57.74,77.56 56.92,77.47Z" />

</vector>
2 changes: 2 additions & 0 deletions core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
<string name="core_thank_you_dialog_negative_description">We received your feedback and will use it to help improve your learning experience going forward. Thank you for sharing!</string>
<string name="core_no_internet_connection">No internet connection</string>
<string name="core_no_internet_connection_description">Please connect to the internet to view this content.</string>
<string name="core_try_again">Try Again</string>
<string name="core_something_went_wrong_description">Something went wrong</string>
<string name="core_ok" tools:ignore="MissingTranslation">OK</string>
<string name="core_continue" tools:ignore="MissingTranslation">Continue</string>
<string name="core_leaving_the_app" tools:ignore="MissingTranslation">Leaving the app</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.webkit.JavascriptInterface
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebSettings
Expand All @@ -18,7 +19,6 @@ import android.webkit.WebViewClient
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
Expand Down Expand Up @@ -53,10 +53,11 @@ import kotlinx.coroutines.launch
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.openedx.core.extension.applyDarkModeIfEnabled
import org.openedx.core.extension.equalsHost
import org.openedx.core.extension.isEmailValid
import org.openedx.core.extension.loadUrl
import org.openedx.core.system.AppCookieManager
import org.openedx.core.ui.ConnectionErrorView
import org.openedx.core.ui.FullScreenErrorView
import org.openedx.core.ui.WindowSize
import org.openedx.core.ui.rememberWindowSize
import org.openedx.core.ui.roundBorderWithoutBottom
Expand Down Expand Up @@ -96,10 +97,6 @@ class HtmlUnitFragment : Fragment() {
OpenEdXTheme {
val windowSize = rememberWindowSize()

var isLoading by remember {
mutableStateOf(true)
}

var hasInternetConnection by remember {
mutableStateOf(viewModel.isOnline)
}
Expand Down Expand Up @@ -148,49 +145,54 @@ class HtmlUnitFragment : Fragment() {
.then(border),
contentAlignment = Alignment.TopCenter
) {
if (uiState.isLoadingEnabled) {
if (uiState is HtmlUnitUIState.Initialization) return@Box
if ((uiState is HtmlUnitUIState.Error).not()) {
if (hasInternetConnection || fromDownloadedContent) {
HTMLContentView(
uiState = uiState,
windowSize = windowSize,
url = url,
cookieManager = viewModel.cookieManager,
apiHostURL = viewModel.apiHostURL,
isLoading = isLoading,
isLoading = uiState is HtmlUnitUIState.Loading,
injectJSList = injectJSList,
onCompletionSet = {
viewModel.notifyCompletionSet()
},
onWebPageLoading = {
isLoading = true
viewModel.onWebPageLoading()
},
onWebPageLoaded = {
isLoading = false
if ((uiState is HtmlUnitUIState.Error).not()) {
viewModel.onWebPageLoaded()
}
if (isAdded) viewModel.setWebPageLoaded(requireContext().assets)
},
onWebPageLoadError = {
viewModel.onWebPageLoadError()
},
saveXBlockProgress = { jsonProgress ->
viewModel.saveXBlockProgress(jsonProgress)
},
)
} else {
ConnectionErrorView(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.background(MaterialTheme.appColors.background)
) {
hasInternetConnection = viewModel.isOnline
}
viewModel.onWebPageLoadError()
}
if (isLoading && hasInternetConnection) {
Box(
modifier = Modifier
.fillMaxSize()
.zIndex(1f),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
} else {
val errorType = (uiState as HtmlUnitUIState.Error).errorType
FullScreenErrorView(errorType = errorType) {
hasInternetConnection = viewModel.isOnline
viewModel.onWebPageLoading()
}
}
if (uiState is HtmlUnitUIState.Loading && hasInternetConnection) {
Box(
modifier = Modifier
.fillMaxSize()
.zIndex(1f),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
}
}
Expand Down Expand Up @@ -239,7 +241,8 @@ private fun HTMLContentView(
onCompletionSet: () -> Unit,
onWebPageLoading: () -> Unit,
onWebPageLoaded: () -> Unit,
saveXBlockProgress: (String) -> Unit
onWebPageLoadError: () -> Unit,
saveXBlockProgress: (String) -> Unit,
) {
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
Expand Down Expand Up @@ -333,6 +336,17 @@ private fun HTMLContentView(
}
super.onReceivedHttpError(view, request, errorResponse)
}

override fun onReceivedError(
view: WebView,
request: WebResourceRequest,
error: WebResourceError
) {
if (view.url.equalsHost(request.url.host)) {
onWebPageLoadError()
}
super.onReceivedError(view, request, error)
}
}
with(settings) {
javaScriptEnabled = true
Expand All @@ -356,7 +370,7 @@ private fun HTMLContentView(
update = { webView ->
if (!isLoading && injectJSList.isNotEmpty()) {
injectJSList.forEach { webView.evaluateJavascript(it, null) }
val jsonProgress = uiState.jsonProgress
val jsonProgress = (uiState as? HtmlUnitUIState.Loaded)?.jsonProgress
if (!jsonProgress.isNullOrEmpty()) {
webView.setupOfflineProgress(jsonProgress)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package org.openedx.course.presentation.unit.html

data class HtmlUnitUIState(
val jsonProgress: String?,
val isLoadingEnabled: Boolean
)
import org.openedx.core.presentation.global.ErrorType

sealed class HtmlUnitUIState {
data object Initialization : HtmlUnitUIState()
data object Loading : HtmlUnitUIState()
data class Loaded(val jsonProgress: String? = null) : HtmlUnitUIState()
data class Error(val errorType: ErrorType) : HtmlUnitUIState()
}
Loading