diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCaptchaFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCaptchaFragment.kt index 3720a41455..0562378ba5 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCaptchaFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCaptchaFragment.kt @@ -16,15 +16,22 @@ package im.vector.app.features.onboarding.ftueauth +import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.view.ViewStub import com.airbnb.mvrx.args +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import im.vector.app.R import im.vector.app.databinding.FragmentFtueLoginCaptchaBinding +import im.vector.app.databinding.ViewStubWebviewBinding import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.OnboardingViewState import im.vector.app.features.onboarding.RegisterAction import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.extensions.orFalse import javax.inject.Inject @Parcelize @@ -40,10 +47,32 @@ class FtueAuthCaptchaFragment @Inject constructor( ) : AbstractFtueAuthFragment() { private val params: FtueAuthCaptchaFragmentArgument by args() + private var webViewBinding: ViewStubWebviewBinding? = null private var isWebViewLoaded = false override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueLoginCaptchaBinding { - return FragmentFtueLoginCaptchaBinding.inflate(inflater, container, false) + return FragmentFtueLoginCaptchaBinding.inflate(inflater, container, false).also { + it.loginCaptchaWebViewStub.setOnInflateListener { _, inflated -> + webViewBinding = ViewStubWebviewBinding.bind(inflated) + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + inflateWebViewOrShowError() + } + + private fun inflateWebViewOrShowError() { + views.loginCaptchaWebViewStub.inflateWebView(onError = { + MaterialAlertDialogBuilder(requireActivity()) + .setTitle(R.string.dialog_title_error) + .setMessage(it.localizedMessage) + .setPositiveButton(R.string.ok) { _, _ -> + requireActivity().recreate() + } + .show() + }) } override fun resetViewModel() { @@ -51,11 +80,38 @@ class FtueAuthCaptchaFragment @Inject constructor( } override fun updateWithState(state: OnboardingViewState) { - if (!isWebViewLoaded) { - captchaWebview.setupWebView(this, views.loginCaptchaWevView, views.loginCaptchaProgress, params.siteKey, state) { + if (!isWebViewLoaded && webViewBinding != null) { + captchaWebview.setupWebView(this, webViewBinding!!.root, views.loginCaptchaProgress, params.siteKey, state) { viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.CaptchaDone(it))) } isWebViewLoaded = true } } } + +private fun ViewStub.inflateWebView(onError: (Throwable) -> Unit) { + try { + inflate() + } catch (e: Exception) { + val isMissingWebView = e.crawlCausesFor { it.message?.contains("MissingWebViewPackageException").orFalse() } + if (isMissingWebView) { + onError(MissingWebViewException(e)) + } else { + onError(e) + } + } +} + +private fun Throwable?.crawlCausesFor(predicate: (Throwable) -> Boolean): Boolean { + return when { + this == null -> false + else -> { + when (predicate(this)) { + true -> true + else -> this.cause.crawlCausesFor(predicate) + } + } + } +} + +private class MissingWebViewException(cause: Throwable) : IllegalStateException("Failed to load WebView provider: No WebView installed", cause) diff --git a/vector/src/main/res/layout/fragment_ftue_login_captcha.xml b/vector/src/main/res/layout/fragment_ftue_login_captcha.xml index 2f6970c785..fc93d0f990 100644 --- a/vector/src/main/res/layout/fragment_ftue_login_captcha.xml +++ b/vector/src/main/res/layout/fragment_ftue_login_captcha.xml @@ -64,15 +64,27 @@ android:id="@+id/titleContentSpacing" android:layout_width="match_parent" android:layout_height="0dp" - app:layout_constraintBottom_toTopOf="@id/loginCaptchaWevView" + app:layout_constraintBottom_toTopOf="@id/loginWebViewBarrier" app:layout_constraintHeight_percent="0.03" app:layout_constraintTop_toBottomOf="@id/captchaHeaderTitle" /> - + + +