diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index e3205e3eade..3c0a0aa3307 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -3,6 +3,7 @@ *** For entries which are touching the Android Wear app's, start entry with `[WEAR]` too. 21.5 ----- +- [**] Enhanced WebView to dynamically scale receipt content, ensuring optimal fit across all device screen sizes. [https://github.com/woocommerce/woocommerce-android/pull/13266] - [*] Fixes missing text in Blaze Campaigns card on larger display and font sizes [https://github.com/woocommerce/woocommerce-android/pull/13300] - [*] Puerto Rico is now available in the list of countries that are supported by in-person payments [https://github.com/woocommerce/woocommerce-android/pull/13200] - [*] Removed the outdated feedback survey for shipping labels [https://github.com/woocommerce/woocommerce-android/pull/13319] diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptHtmlInterceptor.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptHtmlInterceptor.kt new file mode 100644 index 00000000000..72572d7ca83 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptHtmlInterceptor.kt @@ -0,0 +1,17 @@ +package com.woocommerce.android.ui.payments.receipt.preview + +import javax.inject.Inject + +class ReceiptHtmlInterceptor @Inject constructor() { + + fun interceptHtmlContent(originalHtml: String): String { + return if (originalHtml.contains("
")) { + originalHtml.replace( + "", + "" + ) + } else { + originalHtml + } + } +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt index f0103e43069..cb5a938ad24 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt @@ -5,6 +5,8 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View +import android.webkit.WebResourceRequest +import android.webkit.WebResourceResponse import android.webkit.WebView import android.webkit.WebViewClient import androidx.core.view.MenuProvider @@ -18,6 +20,9 @@ import com.woocommerce.android.util.PrintHtmlHelper import com.woocommerce.android.util.UiHelpers import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar import dagger.hilt.android.AndroidEntryPoint +import java.io.IOException +import java.net.MalformedURLException +import java.net.URL import javax.inject.Inject @AndroidEntryPoint @@ -28,6 +33,8 @@ class ReceiptPreviewFragment : BaseFragment(R.layout.fragment_receipt_preview), @Inject lateinit var uiMessageResolver: UIMessageResolver + @Inject lateinit var receiptHtmlInterceptor: ReceiptHtmlInterceptor + private var _binding: FragmentReceiptPreviewBinding? = null private val binding get() = _binding!! @@ -84,16 +91,48 @@ class ReceiptPreviewFragment : BaseFragment(R.layout.fragment_receipt_preview), } else { with(binding.receiptPreviewPreviewWebview) { webViewClient = object : WebViewClient() { + override fun shouldOverrideUrlLoading( + view: WebView, + webResourceRequest: WebResourceRequest + ): Boolean { + return viewModel.isReceiptDomainTrustable(webResourceRequest.url.toString()) + } + + override fun shouldInterceptRequest( + view: WebView, + request: WebResourceRequest + ): WebResourceResponse? { + return interceptAndModifyReceiptResponse(request) + } + override fun onPageFinished(view: WebView, url: String) { viewModel.onReceiptLoaded() } } - settings.loadWithOverviewMode = true - settings.useWideViewPort = true } } } + private fun interceptAndModifyReceiptResponse(request: WebResourceRequest): WebResourceResponse? { + return try { + val connection = URL(request.url.toString()).openConnection() + val inputStream = connection.getInputStream() + val originalHtml = inputStream.bufferedReader().use { it.readText() } + + val modifiedHtml = receiptHtmlInterceptor.interceptHtmlContent(originalHtml) + + WebResourceResponse( + "text/html", + "UTF-8", + modifiedHtml.byteInputStream() + ) + } catch (e: MalformedURLException) { + throw IllegalArgumentException("Invalid receipt URL: ${request.url}", e) + } catch (e: IOException) { + throw IOException("Failed to read content from receipt URL: ${request.url}", e) + } + } + private fun initObservers(binding: FragmentReceiptPreviewBinding) { viewModel.viewStateData.observe(viewLifecycleOwner) { UiHelpers.updateVisibility(binding.receiptPreviewPreviewWebview, it.isContentVisible) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt index 54cf312c698..1ec0915cc4a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import com.woocommerce.android.R.string +import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Content import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Loading @@ -12,11 +13,14 @@ import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.CANCELLED import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.FAILED import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.STARTED +import com.woocommerce.android.util.WooLog import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar import com.woocommerce.android.viewmodel.ScopedViewModel import com.woocommerce.android.viewmodel.navArgs import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import java.net.URI +import java.net.URISyntaxException import javax.inject.Inject @HiltViewModel @@ -25,6 +29,7 @@ class ReceiptPreviewViewModel savedState: SavedStateHandle, private val paymentsFlowTracker: PaymentsFlowTracker, private val paymentReceiptShare: PaymentReceiptShare, + private val selectedSite: SelectedSite, ) : ScopedViewModel(savedState) { private val args: ReceiptPreviewFragmentArgs by savedState.navArgs() @@ -39,6 +44,24 @@ class ReceiptPreviewViewModel viewState.value = Content } + fun isReceiptDomainTrustable(receiptUrl: String): Boolean { + return selectedSite.getIfExists()?.let { site -> + getDomainName(site.url) == getDomainName(receiptUrl) + } ?: false + } + + private fun getDomainName(url: String): String? { + return try { + val uri = URI(url) + uri.host?.let { + if (it.startsWith("www.")) it.substring(WWW_PREFIX_LENGTH) else it + } + } catch (e: URISyntaxException) { + WooLog.e(WooLog.T.ORDERS, "Error parsing domain name from receipt url: $url") + return null + } + } + fun onPrintClicked() { launch { paymentsFlowTracker.trackPrintReceiptTapped() @@ -90,4 +113,8 @@ class ReceiptPreviewViewModel object Loading : ReceiptPreviewViewState(isProgressVisible = true) object Content : ReceiptPreviewViewState(isContentVisible = true) } + + companion object { + private const val WWW_PREFIX_LENGTH = 4 + } } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptHtmlInterceptorTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptHtmlInterceptorTest.kt new file mode 100644 index 00000000000..6ee78d21dc3 --- /dev/null +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptHtmlInterceptorTest.kt @@ -0,0 +1,47 @@ +import com.woocommerce.android.ui.payments.receipt.preview.ReceiptHtmlInterceptor +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class ReceiptHtmlInterceptorTest { + + private val interceptor = ReceiptHtmlInterceptor() + + @Test + fun `given original html, when head is present, then should add viewport meta tag`() { + val originalHtml = """ + +