Skip to content

Commit d970832

Browse files
Merge pull request #14827 from woocommerce/issue/WOOMOB-1585-ipp-setup-authenticated-webview
Use Authenticated WebView for IPP setup
2 parents 610b0c1 + 1188822 commit d970832

File tree

7 files changed

+85
-71
lines changed

7 files changed

+85
-71
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
23.6
55
-----
66
- [Internal] Improve WebView experience by using Authenticated WebView for more scenarios [https://github.com/woocommerce/woocommerce-android/pull/14826]
7+
- [Internal] Update In-Person Payments setup flow to use Authenticated WebView [https://github.com/woocommerce/woocommerce-android/pull/14827]
78

89
23.5
910
-----

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/web/WCWebChromeClient.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.woocommerce.android.ui.compose.component.web
22

33
import android.net.Uri
4+
import android.os.Message
45
import android.webkit.ValueCallback
56
import android.webkit.WebChromeClient
67
import android.webkit.WebView
8+
import android.widget.FrameLayout
79
import androidx.activity.ComponentActivity
810
import androidx.activity.result.contract.ActivityResultContracts
11+
import androidx.core.view.isVisible
912
import com.woocommerce.android.extensions.findActivity
1013

1114
open class WCWebChromeClient : WebChromeClient() {
@@ -48,4 +51,42 @@ open class WCWebChromeClient : WebChromeClient() {
4851
override fun onProgressChanged(view: WebView?, newProgress: Int) {
4952
onProgressChanged(newProgress)
5053
}
54+
55+
override fun onCreateWindow(
56+
view: WebView,
57+
isDialog: Boolean,
58+
isUserGesture: Boolean,
59+
resultMsg: Message
60+
): Boolean {
61+
require(view.parent is FrameLayout) {
62+
"Ensure the WebView is added to a FrameLayout to support popups. Check WCWebView implementation."
63+
}
64+
65+
val popupContainer = view.parent as FrameLayout
66+
67+
val popupWebView = WebView(view.context).apply {
68+
settings.useWideViewPort = view.settings.useWideViewPort
69+
settings.loadWithOverviewMode = view.settings.loadWithOverviewMode
70+
settings.javaScriptEnabled = view.settings.javaScriptEnabled
71+
settings.domStorageEnabled = view.settings.domStorageEnabled
72+
settings.setSupportMultipleWindows(true)
73+
}
74+
75+
popupWebView.webViewClient = view.webViewClient
76+
popupWebView.webChromeClient = object : WebChromeClient() {
77+
override fun onCloseWindow(window: WebView) {
78+
popupContainer.removeView(popupWebView)
79+
view.isVisible = true
80+
}
81+
}
82+
83+
popupContainer.layoutParams = view.layoutParams
84+
popupContainer.addView(popupWebView)
85+
view.isVisible = false
86+
87+
(resultMsg.obj as WebView.WebViewTransport).webView = popupWebView
88+
resultMsg.sendToTarget()
89+
90+
return true
91+
}
5192
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/web/WCWebView.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.woocommerce.android.ui.compose.component.web
33
import android.annotation.SuppressLint
44
import android.view.ViewGroup
55
import android.webkit.WebView
6+
import android.widget.FrameLayout
67
import androidx.activity.compose.BackHandler
78
import androidx.compose.foundation.layout.Box
89
import androidx.compose.foundation.layout.Column
@@ -104,6 +105,7 @@ fun WCWebView(
104105
webView.settings.loadWithOverviewMode = settings.loadWithOverviewMode
105106
webView.settings.javaScriptEnabled = settings.isJavaScriptEnabled
106107
webView.settings.domStorageEnabled = settings.isDomStorageEnabled
108+
webView.settings.setSupportMultipleWindows(true)
107109
}
108110
}
109111

@@ -127,7 +129,7 @@ fun WCWebView(
127129

128130
AndroidView(
129131
factory = { context ->
130-
WebView(context).apply {
132+
val webView = WebView(context).apply {
131133
layoutParams = ViewGroup.LayoutParams(
132134
ViewGroup.LayoutParams.MATCH_PARENT,
133135
ViewGroup.LayoutParams.MATCH_PARENT
@@ -140,6 +142,10 @@ fun WCWebView(
140142

141143
this.settings.userAgentString = userAgent.webViewUserAgent
142144
}.also { webView = it }
145+
146+
FrameLayout(context).apply {
147+
addView(webView)
148+
}
143149
},
144150
modifier = Modifier
145151
.alpha(webViewAlpha)

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingEvent.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ import com.woocommerce.android.viewmodel.MultiLiveEvent
44

55
sealed class CardReaderOnboardingEvent : MultiLiveEvent.Event() {
66
object NavigateToSupport : MultiLiveEvent.Event()
7-
8-
data class NavigateToUrlInBrowser(val url: String) : MultiLiveEvent.Event()
9-
107
data class ContinueToHub(val cardReaderFlowParam: CardReaderFlowParam) : MultiLiveEvent.Event()
118
data class ContinueToConnection(
129
val cardReaderFlowParam: CardReaderFlowParam,

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingFragment.kt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import com.woocommerce.android.extensions.startHelpActivity
2828
import com.woocommerce.android.support.help.HelpOrigin
2929
import com.woocommerce.android.ui.base.BaseFragment
3030
import com.woocommerce.android.ui.base.UIMessageResolver
31+
import com.woocommerce.android.ui.common.webview.AuthenticatedWebViewLauncher
3132
import com.woocommerce.android.ui.main.AppBarStatus
3233
import com.woocommerce.android.util.ChromeCustomTabUtils
3334
import com.woocommerce.android.util.UiHelpers
@@ -43,6 +44,9 @@ class CardReaderOnboardingFragment : BaseFragment(R.layout.fragment_card_reader_
4344
@Inject
4445
lateinit var uiMessageResolver: UIMessageResolver
4546

47+
@Inject
48+
lateinit var authenticatedWebViewLauncher: AuthenticatedWebViewLauncher
49+
4650
val viewModel: CardReaderOnboardingViewModel by viewModels()
4751

4852
override val activityAppBarStatus: AppBarStatus
@@ -92,9 +96,7 @@ class CardReaderOnboardingFragment : BaseFragment(R.layout.fragment_card_reader_
9296
is CardReaderOnboardingEvent.NavigateToSupport -> {
9397
requireActivity().startHelpActivity(HelpOrigin.CARD_READER_ONBOARDING)
9498
}
95-
is CardReaderOnboardingEvent.NavigateToUrlInBrowser -> {
96-
ChromeCustomTabUtils.launchUrl(requireContext(), event.url)
97-
}
99+
98100
is CardReaderOnboardingEvent.ContinueToHub -> {
99101
findNavController().navigate(
100102
CardReaderOnboardingFragmentDirections
@@ -103,6 +105,7 @@ class CardReaderOnboardingFragment : BaseFragment(R.layout.fragment_card_reader_
103105
)
104106
)
105107
}
108+
106109
is CardReaderOnboardingEvent.ContinueToConnection -> {
107110
findNavController().navigate(
108111
CardReaderOnboardingFragmentDirections
@@ -112,6 +115,15 @@ class CardReaderOnboardingFragment : BaseFragment(R.layout.fragment_card_reader_
112115
)
113116
)
114117
}
118+
119+
is MultiLiveEvent.Event.LaunchUrlInAuthenticatedWebView -> {
120+
authenticatedWebViewLauncher.showAuthenticatedWebView(event)
121+
}
122+
123+
is MultiLiveEvent.Event.LaunchUrlInChromeTab -> {
124+
ChromeCustomTabUtils.launchUrl(requireContext(), event.url)
125+
}
126+
115127
is MultiLiveEvent.Event.ShowUiStringSnackbar -> uiMessageResolver.showSnack(event.message)
116128
is MultiLiveEvent.Event.Exit -> findNavController().popBackStack()
117129
else -> event.isHandled = false
@@ -148,20 +160,28 @@ class CardReaderOnboardingFragment : BaseFragment(R.layout.fragment_card_reader_
148160
when (state) {
149161
is CardReaderOnboardingViewState.GenericErrorState ->
150162
showGenericErrorState(layout, state)
163+
151164
is CardReaderOnboardingViewState.NoConnectionErrorState ->
152165
showNetworkErrorState(layout, state)
166+
153167
is CardReaderOnboardingViewState.LoadingState ->
154168
showLoadingState(layout, state)
169+
155170
is CardReaderOnboardingViewState.UnsupportedErrorState ->
156171
showCountryNotSupportedState(layout, state)
172+
157173
is CardReaderOnboardingViewState.WCPayError ->
158174
showWCPayErrorState(layout, state)
175+
159176
is CardReaderOnboardingViewState.StripeAccountError ->
160177
showStripeAccountError(layout, state)
178+
161179
is CardReaderOnboardingViewState.StripeExtensionError ->
162180
showStripeExtensionErrorState(layout, state)
181+
163182
is CardReaderOnboardingViewState.SelectPaymentPluginState ->
164183
showPaymentPluginSelectionState(layout, state)
184+
165185
is CardReaderOnboardingViewState.CashOnDeliveryDisabledState ->
166186
showCashOnDeliveryDisabledState(layout, state)
167187
}
@@ -195,6 +215,7 @@ class CardReaderOnboardingFragment : BaseFragment(R.layout.fragment_card_reader_
195215
R.string.card_reader_onboarding_cash_on_delivery_enable_failure
196216
)
197217
}
218+
198219
null -> {}
199220
}
200221
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingViewModel.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import com.woocommerce.android.model.UiString
1212
import com.woocommerce.android.tools.SelectedSite
1313
import com.woocommerce.android.ui.payments.cardreader.LearnMoreUrlProvider
1414
import com.woocommerce.android.ui.payments.cardreader.LearnMoreUrlProvider.LearnMoreUrlType.IN_PERSON_PAYMENTS
15-
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderOnboardingEvent.NavigateToUrlInBrowser
1615
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderOnboardingParams.Check
1716
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderOnboardingParams.Failed
1817
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderOnboardingState.ChoosePaymentGatewayProvider
@@ -134,7 +133,7 @@ class CardReaderOnboardingViewModel @Inject constructor(
134133
}
135134

136135
is CardReaderOnboardingErrorCtaClickHandler.Reaction.OpenBrowser -> {
137-
triggerEvent(NavigateToUrlInBrowser(reaction.url))
136+
triggerEvent(Event.LaunchUrlInAuthenticatedWebView(reaction.url))
138137
viewState.value = prevState!!
139138
}
140139
}
@@ -438,7 +437,7 @@ class CardReaderOnboardingViewModel @Inject constructor(
438437

439438
private fun onLearnMoreClicked() {
440439
paymentsFlowTracker.trackOnboardingLearnMoreTapped()
441-
triggerEvent(NavigateToUrlInBrowser(learnMoreUrlProvider.provideLearnMoreUrlFor(IN_PERSON_PAYMENTS)))
440+
triggerEvent(Event.LaunchUrlInChromeTab(learnMoreUrlProvider.provideLearnMoreUrlFor(IN_PERSON_PAYMENTS)))
442441
}
443442

444443
private fun onSkipPendingRequirementsClicked() {

WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingViewModelTest.kt

Lines changed: 10 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ class CardReaderOnboardingViewModelTest : BaseUnitTest() {
308308
}
309309

310310
@Test
311-
fun `given handler returned OpenWpComWebView, when clicked on wcpay not setup, then open wp webview`() =
311+
fun `given handler returned OpenBrowser, when clicked on wcpay not setup, then open authenticated webview`() =
312312
testBlocking {
313313
val url = "url"
314314
whenever(errorClickHandler.invoke(CardReaderOnboardingCTAErrorType.WC_PAY_NOT_SETUP))
@@ -328,63 +328,12 @@ class CardReaderOnboardingViewModelTest : BaseUnitTest() {
328328
.actionButtonActionPrimary.invoke()
329329

330330
assertThat(viewModel.event.value).isEqualTo(
331-
CardReaderOnboardingEvent.NavigateToUrlInBrowser(url)
331+
MultiLiveEvent.Event.LaunchUrlInAuthenticatedWebView(url)
332332
)
333333
}
334334

335335
@Test
336-
fun `given handler returned OpenGenericWebView, when clicked on wcpay not setup, then open generic webview`() =
337-
testBlocking {
338-
val url = "url"
339-
whenever(errorClickHandler.invoke(CardReaderOnboardingCTAErrorType.WC_PAY_NOT_SETUP))
340-
.thenReturn(CardReaderOnboardingErrorCtaClickHandler.Reaction.OpenBrowser(url))
341-
342-
val viewModel = createVM(
343-
CardReaderOnboardingFragmentArgs(
344-
CardReaderOnboardingParams.Failed(
345-
cardReaderFlowParam = CardReaderFlowParam.PaymentOrRefund.Payment(1L, ORDER),
346-
onboardingState = SetupNotCompleted(WOOCOMMERCE_PAYMENTS),
347-
),
348-
cardReaderType = CardReaderType.EXTERNAL
349-
).toSavedStateHandle()
350-
)
351-
352-
(viewModel.viewStateData.value as WCPayError.WCPayNotSetupState)
353-
.actionButtonActionPrimary.invoke()
354-
355-
assertThat(viewModel.event.value).isEqualTo(
356-
CardReaderOnboardingEvent.NavigateToUrlInBrowser(url)
357-
)
358-
}
359-
360-
@Test
361-
fun `given handler returned OpenWpComWebView, when clicked on stripe req overdue, then open wpcom webview`() =
362-
testBlocking {
363-
val url = "url"
364-
whenever(errorClickHandler.invoke(CardReaderOnboardingCTAErrorType.STRIPE_ACCOUNT_OVERDUE_REQUIREMENTS))
365-
.thenReturn(CardReaderOnboardingErrorCtaClickHandler.Reaction.OpenBrowser(url))
366-
367-
val viewModel = createVM(
368-
CardReaderOnboardingFragmentArgs(
369-
CardReaderOnboardingParams.Failed(
370-
cardReaderFlowParam =
371-
CardReaderFlowParam.PaymentOrRefund.Payment(1L, ORDER),
372-
onboardingState = StripeAccountOverdueRequirement(WOOCOMMERCE_PAYMENTS),
373-
),
374-
cardReaderType = CardReaderType.EXTERNAL
375-
).toSavedStateHandle()
376-
)
377-
378-
(viewModel.viewStateData.value as StripeAccountError.StripeAccountOverdueRequirementsState)
379-
.actionButtonPrimary!!.action.invoke()
380-
381-
assertThat(viewModel.event.value).isEqualTo(
382-
CardReaderOnboardingEvent.NavigateToUrlInBrowser(url)
383-
)
384-
}
385-
386-
@Test
387-
fun `given handler returned OpenGenericWebView, when clicked on stripe req overdue, then open generic webview`() =
336+
fun `given handler returned OpenBrowser, when clicked on stripe req overdue, then open authenticated webview`() =
388337
testBlocking {
389338
val url = "url"
390339
whenever(errorClickHandler.invoke(CardReaderOnboardingCTAErrorType.STRIPE_ACCOUNT_OVERDUE_REQUIREMENTS))
@@ -404,7 +353,7 @@ class CardReaderOnboardingViewModelTest : BaseUnitTest() {
404353
.actionButtonPrimary!!.action.invoke()
405354

406355
assertThat(viewModel.event.value).isEqualTo(
407-
CardReaderOnboardingEvent.NavigateToUrlInBrowser(url)
356+
MultiLiveEvent.Event.LaunchUrlInAuthenticatedWebView(url)
408357
)
409358
}
410359

@@ -623,7 +572,7 @@ class CardReaderOnboardingViewModelTest : BaseUnitTest() {
623572
(viewModel.viewStateData.value as UnsupportedErrorState.WcPayInCountry)
624573
.onLearnMoreActionClicked.invoke()
625574

626-
val event = viewModel.event.value as CardReaderOnboardingEvent.NavigateToUrlInBrowser
575+
val event = viewModel.event.value as MultiLiveEvent.Event.LaunchUrlInChromeTab
627576
assertThat(event.url).isEqualTo(AppUrls.WOOCOMMERCE_LEARN_MORE_ABOUT_PAYMENTS)
628577
}
629578

@@ -637,7 +586,7 @@ class CardReaderOnboardingViewModelTest : BaseUnitTest() {
637586
(viewModel.viewStateData.value as UnsupportedErrorState.Country)
638587
.onLearnMoreActionClicked.invoke()
639588

640-
val event = viewModel.event.value as CardReaderOnboardingEvent.NavigateToUrlInBrowser
589+
val event = viewModel.event.value as MultiLiveEvent.Event.LaunchUrlInChromeTab
641590
assertThat(event.url).isEqualTo(AppUrls.WOOCOMMERCE_LEARN_MORE_ABOUT_PAYMENTS)
642591
}
643592

@@ -690,7 +639,7 @@ class CardReaderOnboardingViewModelTest : BaseUnitTest() {
690639

691640
(viewModel.viewStateData.value as UnsupportedErrorState.Country).onLearnMoreActionClicked.invoke()
692641

693-
val event = viewModel.event.value as CardReaderOnboardingEvent.NavigateToUrlInBrowser
642+
val event = viewModel.event.value as MultiLiveEvent.Event.LaunchUrlInChromeTab
694643
assertThat(event.url).isEqualTo(AppUrls.STRIPE_LEARN_MORE_ABOUT_PAYMENTS)
695644
}
696645

@@ -706,7 +655,7 @@ class CardReaderOnboardingViewModelTest : BaseUnitTest() {
706655

707656
(viewModel.viewStateData.value as UnsupportedErrorState.Country).onLearnMoreActionClicked.invoke()
708657

709-
val event = viewModel.event.value as CardReaderOnboardingEvent.NavigateToUrlInBrowser
658+
val event = viewModel.event.value as MultiLiveEvent.Event.LaunchUrlInChromeTab
710659
assertThat(event.url).isEqualTo(AppUrls.WOOCOMMERCE_LEARN_MORE_ABOUT_PAYMENTS)
711660
}
712661

@@ -720,7 +669,7 @@ class CardReaderOnboardingViewModelTest : BaseUnitTest() {
720669
(viewModel.viewStateData.value as UnsupportedErrorState.StripeAccountInUnsupportedCountry)
721670
.onLearnMoreActionClicked.invoke()
722671

723-
val event = viewModel.event.value as CardReaderOnboardingEvent.NavigateToUrlInBrowser
672+
val event = viewModel.event.value as MultiLiveEvent.Event.LaunchUrlInChromeTab
724673
assertThat(event.url).isEqualTo(AppUrls.WOOCOMMERCE_LEARN_MORE_ABOUT_PAYMENTS)
725674
}
726675

@@ -1113,7 +1062,7 @@ class CardReaderOnboardingViewModelTest : BaseUnitTest() {
11131062
(viewModel.viewStateData.value as CashOnDeliveryDisabledState)
11141063
.onLearnMoreActionClicked.invoke()
11151064

1116-
val event = viewModel.event.value as CardReaderOnboardingEvent.NavigateToUrlInBrowser
1065+
val event = viewModel.event.value as MultiLiveEvent.Event.LaunchUrlInChromeTab
11171066
assertThat(event.url).isEqualTo(AppUrls.WOOCOMMERCE_LEARN_MORE_ABOUT_PAYMENTS)
11181067
}
11191068

0 commit comments

Comments
 (0)