Skip to content

Commit

Permalink
refactor: support info section (openedx#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
k1rill authored Dec 13, 2023
1 parent cd27754 commit 0fd9238
Show file tree
Hide file tree
Showing 15 changed files with 1,285 additions and 1,062 deletions.
5 changes: 5 additions & 0 deletions app/src/main/java/org/openedx/app/AnalyticsManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ class AnalyticsManager(
logEvent(Event.PRIVACY_POLICY_CLICKED)
}

override fun termsOfUseClickedEvent() {
logEvent(Event.TERMS_OF_USE_CLICKED)
}

override fun cookiePolicyClickedEvent() {
logEvent(Event.COOKIE_POLICY_CLICKED)
}
Expand Down Expand Up @@ -415,6 +419,7 @@ private enum class Event(val eventName: String) {
PROFILE_DELETE_ACCOUNT_CLICKED("Profile_Delete_Account_Clicked"),
PROFILE_VIDEO_SETTINGS_CLICKED("Profile_Video_settings_Clicked"),
PRIVACY_POLICY_CLICKED("Privacy_Policy_Clicked"),
TERMS_OF_USE_CLICKED("Terms_Of_Use_Clicked"),
COOKIE_POLICY_CLICKED("Cookie_Policy_Clicked"),
EMAIL_SUPPORT_CLICKED("Email_Support_Clicked"),
COURSE_ENROLL_CLICKED("Course_Enroll_Clicked"),
Expand Down
12 changes: 10 additions & 2 deletions app/src/main/java/org/openedx/app/AppRouter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import org.openedx.core.domain.model.CoursewareAccess
import org.openedx.core.presentation.course.CourseViewMode
import org.openedx.core.presentation.global.app_upgrade.AppUpgradeRouter
import org.openedx.core.presentation.global.app_upgrade.UpgradeRequiredFragment
import org.openedx.core.presentation.global.webview.WebContentFragment
import org.openedx.course.presentation.CourseRouter
import org.openedx.course.presentation.container.CourseContainerFragment
import org.openedx.course.presentation.container.NoAccessCourseContainerFragment
import org.openedx.course.presentation.detail.CourseDetailsFragment
import org.openedx.course.presentation.handouts.HandoutsType
import org.openedx.course.presentation.handouts.WebViewFragment
import org.openedx.course.presentation.handouts.HandoutsWebViewFragment
import org.openedx.course.presentation.section.CourseSectionFragment
import org.openedx.course.presentation.unit.container.CourseUnitContainerFragment
import org.openedx.course.presentation.unit.video.VideoFullScreenFragment
Expand Down Expand Up @@ -223,7 +224,7 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di
) {
replaceFragmentWithBackStack(
fm,
WebViewFragment.newInstance(title, type.name, courseId)
HandoutsWebViewFragment.newInstance(title, type.name, courseId)
)
}
//endregion
Expand Down Expand Up @@ -307,6 +308,13 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di
replaceFragmentWithBackStack(fm, DeleteProfileFragment())
}

override fun navigateToWebContent(fm: FragmentManager, title: String, url: String) {
replaceFragmentWithBackStack(
fm,
WebContentFragment.newInstance(title = title, url = url)
)
}

override fun restartApp(fm: FragmentManager, isLogistrationEnabled: Boolean) {
fm.apply {
for (fragment in fragments) {
Expand Down
20 changes: 11 additions & 9 deletions app/src/main/java/org/openedx/app/di/ScreenModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,17 @@ val screenModule = module {
factory { ProfileInteractor(get()) }
viewModel {
ProfileViewModel(
get(),
get(),
get(),
get(),
get(named("IODispatcher")),
get(),
get(),
get(),
get()
appData = get(),
config = get(),
interactor = get(),
resourceManager = get(),
notifier = get(),
dispatcher = get(named("IODispatcher")),
cookieManager = get(),
workerController = get(),
analytics = get(),
appUpgradeNotifier = get(),
router = get(),
)
}
viewModel { (account: Account) -> EditProfileViewModel(get(), get(), get(), get(), account) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.openedx.core.presentation.global.webview

import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import org.openedx.core.ui.WebContentScreen
import org.openedx.core.ui.rememberWindowSize
import org.openedx.core.ui.theme.OpenEdXTheme

class WebContentFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
OpenEdXTheme {
val windowSize = rememberWindowSize()
WebContentScreen(
windowSize = windowSize,
title = requireArguments().getString(ARG_TITLE, ""),
contentUrl = requireArguments().getString(ARG_URL, ""),
onBackClick = {
requireActivity().supportFragmentManager.popBackStack()
})
}
}
}

companion object {
private const val ARG_TITLE = "argTitle"
private const val ARG_URL = "argUrl"

fun newInstance(title: String, url: String): WebContentFragment {
val fragment = WebContentFragment()
fragment.arguments = bundleOf(
ARG_TITLE to title,
ARG_URL to url,
)
return fragment
}
}
}
213 changes: 213 additions & 0 deletions core/src/main/java/org/openedx/core/ui/WebContentScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package org.openedx.core.ui

import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.webkit.WebResourceRequest
import android.webkit.WebView
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.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
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.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
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.isEmailValid
import org.openedx.core.extension.replaceLinkTags
import org.openedx.core.ui.theme.appColors
import org.openedx.core.ui.theme.appTypography
import org.openedx.core.utils.EmailUtil
import java.nio.charset.StandardCharsets

@Composable
fun WebContentScreen(
windowSize: WindowSize,
apiHostUrl: String? = null,
title: String,
onBackClick: () -> Unit,
htmlBody: String? = null,
contentUrl: String? = null,
) {
val scaffoldState = rememberScaffoldState()
Scaffold(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 16.dp),
scaffoldState = scaffoldState,
backgroundColor = MaterialTheme.appColors.background
) {

val screenWidth by remember(key1 = windowSize) {
mutableStateOf(
windowSize.windowSizeValue(
expanded = Modifier.widthIn(Dp.Unspecified, 560.dp),
compact = Modifier.fillMaxWidth()
)
)
}

Box(
modifier = Modifier
.fillMaxWidth()
.padding(it)
.statusBarsInset()
.displayCutoutForLandscape(),
contentAlignment = Alignment.TopCenter
) {
Column(screenWidth) {
Box(
Modifier
.fillMaxWidth()
.zIndex(1f),
contentAlignment = Alignment.CenterStart
) {
BackBtn {
onBackClick()
}

Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 56.dp),
text = title,
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center
)
}
Spacer(Modifier.height(6.dp))
Surface(
Modifier.fillMaxSize(),
color = MaterialTheme.appColors.background
) {
if (htmlBody.isNullOrEmpty() && contentUrl.isNullOrEmpty()) {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.appColors.background)
.zIndex(1f),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
} else {
var webViewAlpha by rememberSaveable { mutableFloatStateOf(0f) }
Surface(
Modifier
.padding(horizontal = 16.dp, vertical = 24.dp)
.alpha(webViewAlpha),
color = MaterialTheme.appColors.background
) {
WebViewContent(
apiHostUrl = apiHostUrl,
body = htmlBody,
contentUrl = contentUrl,
onWebPageLoaded = {
webViewAlpha = 1f
})
}
}
}
}
}
}
}

@Composable
@SuppressLint("SetJavaScriptEnabled")
private fun WebViewContent(
apiHostUrl: String? = null,
body: String? = null,
contentUrl: String? = null,
onWebPageLoaded: () -> Unit
) {
val context = LocalContext.current
val isDarkTheme = isSystemInDarkTheme()
AndroidView(
factory = {
WebView(context).apply {
webViewClient = object : WebViewClient() {
override fun onPageCommitVisible(view: WebView?, url: String?) {
super.onPageCommitVisible(view, url)
onWebPageLoaded()
}

override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
val clickUrl = request?.url?.toString() ?: ""
return if (clickUrl.isNotEmpty() &&
(clickUrl.startsWith("http://") ||
clickUrl.startsWith("https://"))
) {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(clickUrl)))
true
} else if (clickUrl.startsWith("mailto:")) {
val email = clickUrl.replace("mailto:", "")
if (email.isEmailValid()) {
EmailUtil.sendEmailIntent(context, email, "", "")
true
} else {
false
}
} else {
false
}
}
}
with(settings) {
javaScriptEnabled = true
loadWithOverviewMode = true
builtInZoomControls = false
setSupportZoom(true)
loadsImagesAutomatically = true
domStorageEnabled = true
}
isVerticalScrollBarEnabled = false
isHorizontalScrollBarEnabled = false
body?.let {
loadDataWithBaseURL(
apiHostUrl,
body.replaceLinkTags(isDarkTheme),
"text/html",
StandardCharsets.UTF_8.name(),
null
)
}
contentUrl?.let {
loadUrl(it)
}
}
},
)
}
12 changes: 7 additions & 5 deletions core/src/main/java/org/openedx/core/utils/EmailUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.openedx.core.utils
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.widget.Toast
import org.openedx.core.R
Expand Down Expand Up @@ -37,11 +38,12 @@ object EmailUtil {
subject: String,
email: String
) {
val emailIntent = Intent(Intent.ACTION_SEND)
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(to))
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject)
emailIntent.putExtra(Intent.EXTRA_TEXT, email)
emailIntent.type = "plain/text"
val emailIntent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("mailto:")
putExtra(Intent.EXTRA_EMAIL, arrayOf(to))
putExtra(Intent.EXTRA_SUBJECT, subject)
putExtra(Intent.EXTRA_TEXT, email)
}
try {
emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context?.let {
Expand Down
Loading

0 comments on commit 0fd9238

Please sign in to comment.