Почему при открытии pdf ссылки не срабатывает parentRouter.navigateTo(externalUrlScreen)? package ru.maxi.mobile.ui.delivery.order.onlinepayment


import ru.maxi.mobile.utils.services.isGooglePlayServicesAvailable
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import android.webkit.DownloadListener
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.addCallback
import androidx.core.os.BundleCompat
import androidx.core.os.bundleOf
import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.fragment.app.setFragmentResult
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import by.kirich1409.viewbindingdelegate.viewBinding
import kotlinx.coroutines.launch
import ru.maxi.mobile.R
import ru.maxi.mobile.app
import ru.maxi.mobile.base.ui.MvvmBaseFragment
import ru.maxi.mobile.base.ui.manager.showConfirmDialog
import ru.maxi.mobile.core.navigation.parentRouter
import ru.maxi.mobile.databinding.FragmentWebviewBinding
import ru.maxi.mobile.entity.delivery.order.onlinepayment.OnlinePaymentData
import ru.maxi.mobile.entity.delivery.order.onlinepayment.OnlinePaymentFormType
import ru.maxi.mobile.presentation.common.NegativeButton
import ru.maxi.mobile.presentation.common.PositiveButton
import ru.maxi.mobile.presentation.common.screens.CommonScreens
import ru.maxi.mobile.ui.common.BackButtonListener
import ru.maxi.mobile.ui.delivery.order.onlinepayment.entity.OnlinePaymentFormNavigationEvent
import ru.maxi.mobile.ui.delivery.order.onlinepayment.entity.OnlinePaymentFormViewState
import ru.maxi.mobile.utils.assistedViewModels
import javax.inject.Inject

/**
 * Экран формы онлайн оплаты заказа
 */
class OnlinePaymentFormFragment : MvvmBaseFragment(R.layout.fragment_webview), BackButtonListener {

    @Inject
    lateinit var viewModelFactory: OnlinePaymentFormViewModel.Factory

    private val viewBinding by viewBinding(FragmentWebviewBinding::bind)

    private val paymentData: OnlinePaymentData by lazy {
        requireNotNull(
            BundleCompat.getParcelable(requireArguments(), ARG_PAYMENT_DATA, OnlinePaymentData::class.java),
        )
    }

    private val showCloseButton: Boolean by lazy {
        paymentData.formType is OnlinePaymentFormType.OrderPayment
    }

    private val viewModel: OnlinePaymentFormViewModel by assistedViewModels {
        viewModelFactory.create(paymentData)
    }

    override val analyticsName: String = "_OnlinePaymentForm"

    override fun onAttach(context: Context) {
        super.onAttach(context)
        app.deliveryComponent.inject(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (showCloseButton) {
            activity?.onBackPressedDispatcher?.addCallback(this) {
                showExitConfirmDialog()
            }
        }

        lifecycleScope.launchWhenStarted {
            viewModel.viewState.collect(::onViewStateChange)
        }
    }

    @SuppressLint("SetJavaScriptEnabled")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        actionBarManager.setTitle(R.string.delivery_makeorder_payment_online_form_title)
        actionBarManager.showBackButton(withCloseIcon = showCloseButton)

        with(viewBinding) {
            webView.apply {
                settings.apply {
                    textZoom = 100
                    javaScriptEnabled = true
                    domStorageEnabled = true
                }
                webViewClient = CustomWebViewClient()
                setDownloadListener(CustomDownloadListener())
            }
            noConnectionView.setOnActionButtonClickListener {
                webView.reload()
            }
        }

        with(viewLifecycleOwner) {
            lifecycleScope.launch {
                repeatOnLifecycle(Lifecycle.State.STARTED) {
                    viewModel.navigationEvent.collect(::onNavigationEvent)
                }
            }
        }
    }

    override fun onBackPressed(): Boolean {
        paymentData.formType?.let { formType ->
            if (formType is OnlinePaymentFormType.AddNewCard) {
                val data = Bundle.EMPTY
                setFragmentResult(formType.resultRequestKey, data)
            }
        }
        parentRouter.exit()
        return true
    }

    private fun onViewStateChange(state: OnlinePaymentFormViewState) {
        when (state) {
            is OnlinePaymentFormViewState.Loading -> with(viewBinding) {
                webView.isInvisible = true
                noConnectionView.isGone = true
                loaderView.show()
                state.newPageUrl?.let(webView::loadUrl)
            }

            is OnlinePaymentFormViewState.Loaded -> with(viewBinding) {
                webView.isVisible = true
                noConnectionView.isGone = true
                loaderView.hide()
            }

            OnlinePaymentFormViewState.Error -> with(viewBinding) {
                webView.isInvisible = true
                noConnectionView.isVisible = true
                loaderView.hide()
            }
        }
    }

    private fun onNavigationEvent(event: OnlinePaymentFormNavigationEvent) {
        when (event) {
            OnlinePaymentFormNavigationEvent.Exit -> exit()
        }
    }

    private fun showExitConfirmDialog() {
        dialogManager.showConfirmDialog(
            getString(R.string.delivery_makeorder_payment_online_cancel_confirm_message),
            PositiveButton(getString(R.string.btn_yes), ::exit),
            NegativeButton(getString(R.string.btn_no)),
        )
    }

    private fun exit() {
        paymentData.formType?.let { formType ->
            val data = when (formType) {
                is OnlinePaymentFormType.AddNewCard -> Bundle.EMPTY
                is OnlinePaymentFormType.OrderPayment -> bundleOf(
                    OnlinePaymentFormType.OrderPayment.RESULT_DATA_ORDER_ID_KEY to formType.orderId,
                )
            }
            setFragmentResult(formType.resultRequestKey, data)
        }
        parentRouter.exit()
    }

    private inner class CustomWebViewClient : WebViewClient() {

        override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
            val uri = request.url
            Log.d("TESTING", "shouldOverrideUrlLoading")
            return when {
                uri.scheme == SBER_PAY_DEEPLINK_SCHEME -> {
                    Log.d("TESTING", "shouldOverrideUrlLoading SBER_PAY_DEEPLINK_SCHEME")

                    if (isSberBankAppInstalled()) {
                        parentRouter.navigateTo(CommonScreens.ExternalUriScreen(uri))
                    }
                    true
                }

                uri.host == PLAY_MARKET_HOST_NAME -> {
                    Log.d("TESTING", "shouldOverrideUrlLoading PLAY_MARKET_HOST_NAME")

                    if (!isSberBankAppInstalled()) {
                        if (requireContext().isGooglePlayServicesAvailable) {
                            parentRouter.navigateTo(CommonScreens.PlayMarketScreen(uri))
                        }
                    }
                    true
                }

                else -> {
                    Log.d("TESTING", "shouldOverrideUrlLoading else")
                    super.shouldOverrideUrlLoading(view, request)}
            }
        }


        private fun isSberBankAppInstalled(): Boolean {
            val packageManager = context?.packageManager ?: return false
            return try {
                packageManager.getPackageInfo(SBER_BANK_APP_PACKAGE_NAME, 0)
                true
            } catch (e: PackageManager.NameNotFoundException) {
                false
            }
        }

        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
            viewModel.onPageStarted()
        }

        override fun onPageFinished(view: WebView?, url: String?) {
            url?.let(viewModel::onFinishLoad)
        }

        override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
            if (request.isForMainFrame) {
                viewModel.onPageLoadError()
            }
        }

        override fun onReceivedHttpError(
            view: WebView,
            request: WebResourceRequest,
            errorResponse: WebResourceResponse,
        ) {
            if (request.isForMainFrame) {
                viewModel.onPageLoadError()
            }
        }
    }

    private inner class CustomDownloadListener : DownloadListener {
        override fun onDownloadStart(
            url: String?,
            userAgent: String?,
            contentDisposition: String?,
            mimeType: String?,
            contentLength: Long,
        ) {
            Log.d("TESTING", "CustomDownloadListener")
            if (url != null) {
                Log.d("TESTING", "CustomDownloadListener if url != null")
                val externalUrlScreen = CommonScreens.ExternalUriScreen(Uri.parse(url), mimeType)
                parentRouter.navigateTo(externalUrlScreen)
            }
        }
    }

    companion object {

        private const val PLAY_MARKET_HOST_NAME = "play.google.com"
        private const val SBER_BANK_APP_PACKAGE_NAME = "ru.sberbankmobile"
        private const val SBER_PAY_DEEPLINK_SCHEME = "sberpay"

        private const val ARG_PAYMENT_DATA = "ARG_PAYMENT_DATA"

        fun getInstance(paymentData: OnlinePaymentData): OnlinePaymentFormFragment {
            return OnlinePaymentFormFragment().apply {
                arguments = Bundle().apply {
                    putParcelable(ARG_PAYMENT_DATA, paymentData)
                }
            }
        }
    }
}
package ru.maxi.mobile.presentation.common.screens

import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import com.axmor.maxi.UserPolicyActivity
import com.axmor.maxi.view.main.MainNavigationActivity
import com.axmor.maxi.view.video.YouTubePlayerActivity
import com.github.terrakok.cicerone.androidx.ActivityScreen
import com.github.terrakok.cicerone.androidx.FragmentScreen
import ru.maxi.mobile.common.utils.android.ChooserIntent
import ru.maxi.mobile.common.utils.android.barcode.BarcodeType
import ru.maxi.mobile.core.navigation.BottomSheetFragmentScreenWithArgs
import ru.maxi.mobile.core.navigation.FragmentScreenWithArgs
import ru.maxi.mobile.feature.advertising.model.AdvertisingData
import ru.maxi.mobile.feature.advertising.view.AdvertisingArgs
import ru.maxi.mobile.feature.advertising.view.AdvertisingFragment
import ru.maxi.mobile.feature.analytics.model.eventsource.PostEventSource
import ru.maxi.mobile.feature.barcodeScan.view.BarcodeScanArgs
import ru.maxi.mobile.feature.barcodeScan.view.BarcodeScanFragment
import ru.maxi.mobile.feature.learning.LearningActivity
import ru.maxi.mobile.feature.post.view.PostDetailFragment
import ru.maxi.mobile.feature.productCollection.ui.ProductCollectionFragment
import ru.maxi.mobile.feature.productCollection.ui.ProductCollectionViewData
import ru.maxi.mobile.feature.productCollection.ui.personal.PersonalProductCollectionFragment
import ru.maxi.mobile.feature.webfeature.model.WebFeature
import ru.maxi.mobile.feature.webfeature.view.WebFeatureFragment
import ru.maxi.mobile.feature.webfeature.view.model.WebFeatureScreenArgs
import ru.maxi.mobile.feature.webview.ui.WebViewArgs
import ru.maxi.mobile.feature.webview.ui.WebViewFragment
import ru.maxi.mobile.utils.getInstallerPackageName
import java.net.URI

/**
 * Общие экраны для всего приложения
 */
@Suppress("FunctionName")
object CommonScreens {

    /**
     * Экран "Рекламное объявление"
     */
    fun AdvertisingScreen(
        advertisingData: AdvertisingData,
    ) = BottomSheetFragmentScreenWithArgs(
        args = AdvertisingArgs(advertisingData),
    ) {
        AdvertisingFragment()
    }

    /**
     * Экран сканирования QR и штрих кодов
     * @param resultKey ключ для отправки результата в текущий роутер
     * @property barcodeType тип баркода
     */
    fun BarcodeScanningScreen(
        resultKey: String,
        barcodeType: BarcodeType,
    ) = FragmentScreenWithArgs(
        args = BarcodeScanArgs(resultKey, barcodeType),
    ) {
        BarcodeScanFragment()
    }

    /**
     * Экран обучение приложения
     */
    fun LearningScreen() = ActivityScreen {
        Intent(it, LearningActivity::class.java)
    }

    /**
     * Экран для перехода на главный экран
     */
    fun MainApplicationScreen() = ActivityScreen {
        val intent = Intent(it, MainNavigationActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        intent
    }

    /**
     * Экран вызова звонка на номер [phone]
     */
    fun CallScreen(phone: String) = ActivityScreen {
        val intent = Intent(Intent.ACTION_DIAL)
        intent.data = Uri.parse("tel:$phone")
        intent
    }

    /**
     * Экран YouTube-плеера для видео с идентификатором [videoId]
     */
    fun YouTubePlayerScreen(videoId: String) = ActivityScreen {
        YouTubePlayerActivity.getIntent(it, videoId)
    }

    /**
     * Открыть экран настроек приложения Макси в системе
     */
    fun AppSystemSettingsScreen() = ActivityScreen { context ->
        val uri = Uri.fromParts("package", context.packageName, null)
        Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, uri)
    }

    /**
     * Открыть страницу приложения [appMarketUri] в Google Play Store
     */
    fun PlayMarketScreen(appMarketUri: Uri) = ActivityScreen {
        val intent = Intent(Intent.ACTION_VIEW)
        intent.data = appMarketUri
        intent.setPackage(PLAY_MARKET_APP_PACKAGE_NAME)
        intent
    }

    /**
     * Открыть страницу приложения с идентификатором [appId] в магазине приложений
     *
     * Будет открыт магазин приложений, установивший текущее приложение
     *
     * Если определить магазин приложений не удалось - отобразится выбор приложений,
     * поддерживающих открытие ссылки на приложение
     *
     * Если установивший текущее приложение магазин приложений
     * не поддерживает открытие ссылки на приложение - ничего не произойдёт
     */
    fun MarketScreen(appId: String) = ActivityScreen { context ->
        Intent(Intent.ACTION_VIEW).apply {
            setPackage(context.getInstallerPackageName())
            data = Uri.parse("market://details?id=$appId")
        }
    }

    /**
     * Открыть приложение Макси в магазине приложений
     */
    fun AppMarketScreen() = ActivityScreen { context ->
        MarketScreen(appId = context.packageName).createIntent(context)
    }

    /**
     * Открыть внешнюю ссылку [url]
     */
    fun ExternalUrlScreen(url: String) = ExternalUriScreen(Uri.parse(url))

    /**
     * Открыть внешний ресурс по ссылке [uri]
     */
    fun ExternalUriScreen(uri: Uri, mimetype: String? = null) = ActivityScreen { context ->
        val intent = Intent(Intent.ACTION_VIEW, uri).apply {
            addCategory(Intent.CATEGORY_BROWSABLE)
            setDataAndType(uri, mimetype)
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            ChooserIntent(context = context, targetIntent = intent, extraIntents = listOf(intent))
        } else {
            intent
        }
    }

    /**
     * Экран правил пользования аккаунтом
     */
    fun UserPolicyScreen(onlyShow: Boolean = false) = ActivityScreen {
        val intent = Intent(it, UserPolicyActivity::class.java)
        intent.putExtra(UserPolicyActivity.ONLY_SHOW, onlyShow)
        if (!onlyShow) {
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }
        intent
    }

    /**
     * Экран деталки поста с идентификатором [postId]
     *
     * @param clickOnVideo флаг клика по видео поста
     */
    @JvmOverloads
    fun PostDetailScreen(
        postId: Long,
        clickOnVideo: Boolean = false,
        eventSource: PostEventSource,
    ) = FragmentScreen {
        PostDetailFragment.getInstance(postId, clickOnVideo, eventSource)
    }

    /**
     * Экран товарной подборки с данными [collectionData]
     */
    fun ProductCollectionScreen(collectionData: ProductCollectionViewData) = FragmentScreen {
        ProductCollectionFragment.getInstance(collectionData)
    }

    /**
     * Экран персональной товарной подборки с данными [collectionData]
     */
    fun PersonalProductCollectionScreen(collectionData: ProductCollectionViewData) = FragmentScreen {
        PersonalProductCollectionFragment.getInstance(collectionData)
    }

    /**
     * Экран для Web-фичи [featureType]
     */
    fun WebFeatureScreen(
        featureType: WebFeature,
    ) = FragmentScreenWithArgs(
        args = WebFeatureScreenArgs(featureType),
    ) { WebFeatureFragment() }

    /**
     * Экран для WebView со ссылкой [url] и заголовком [title]
     */
    fun WebViewScreen(
        uri: URI,
        title: String,
    ) = FragmentScreenWithArgs(
        args = WebViewArgs(uri, title),
    ) { WebViewFragment() }

    /**
     * Экран для WebView документа по ключу [key] и заголовком [title]
     */
    fun ExternalDocumentWebViewScreen(key: ExternalDocumentKey, title: String) =
        WebViewScreen(URI.create("api/v1/documents?key=${key.name}"), title)

    /**
     * Значения ключей для документов `ExternalDocumentWebViewScreen`
     */
    enum class ExternalDocumentKey {
        OFFER,
        PERSONAL_DATA,
        REGISTRATION_ADVERTISING_KEY,
        REGISTRATION_LOYALTY_PROGRAM_KEY,
        REGISTRATION_PERSONAL_DATA_KEY,
    }

    private const val PLAY_MARKET_APP_PACKAGE_NAME = "com.android.vending"
}