| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- package com.adealink.weparty.webview
- import android.content.Intent
- import android.graphics.Bitmap
- import android.graphics.Outline
- import android.net.http.SslError
- import android.os.Bundle
- import android.view.KeyEvent
- import android.view.View
- import android.view.ViewOutlineProvider
- import android.webkit.CookieManager
- import android.webkit.SslErrorHandler
- import androidx.core.net.toUri
- import androidx.fragment.app.FragmentManager
- import androidx.lifecycle.LifecycleOwner
- import com.adealink.frame.data.json.toJsonErrorNull
- import com.adealink.frame.ext.isViewBindingValid
- import com.adealink.frame.locale.language.languageManager
- import com.adealink.frame.log.Log
- import com.adealink.frame.mvvm.view.viewBinding
- import com.adealink.frame.network.http.interceptor.KEY_API
- import com.adealink.frame.network.http.interceptor.KEY_CHANNEL
- import com.adealink.frame.network.http.interceptor.KEY_DEVICE
- import com.adealink.frame.network.http.interceptor.KEY_NETWORK
- import com.adealink.frame.network.http.interceptor.KEY_PACKAGE_NAME
- import com.adealink.frame.network.http.interceptor.KEY_PLATFORM
- import com.adealink.frame.network.http.interceptor.KEY_TIME
- import com.adealink.frame.network.http.interceptor.KEY_TOKEN
- import com.adealink.frame.network.http.interceptor.KEY_UDID
- import com.adealink.frame.network.http.interceptor.KEY_VERSION_CODE
- import com.adealink.frame.router.Router
- import com.adealink.frame.router.annotation.BindExtra
- import com.adealink.frame.util.AppUtil
- import com.adealink.frame.util.PackageUtil
- import com.adealink.frame.util.getDeviceName
- import com.adealink.frame.util.naviBarHeightDp
- import com.adealink.frame.util.statusBarHeightDp
- import com.adealink.weparty.App
- import com.adealink.weparty.BuildConfig
- import com.adealink.weparty.R
- import com.adealink.weparty.commonui.BaseFragment
- import com.adealink.weparty.commonui.ext.dp
- import com.adealink.weparty.databinding.FragmentWebviewBinding
- import com.adealink.weparty.module.webview.Web
- import com.adealink.weparty.module.webview.Web.Common.Companion.EXTRA_TOP_RADIUS
- import com.adealink.weparty.storage.AppPref
- import com.adealink.weparty.webview.callback.IWebViewCallback
- import com.adealink.weparty.webview.callback.IWebViewFragmentCallback
- import com.adealink.weparty.webview.component.ErrorComp
- import com.adealink.weparty.webview.constant.NATIVE_JS_BRIDGE
- import com.adealink.weparty.webview.constant.NavigationBarMode
- import com.adealink.weparty.webview.constant.TAG_WEB_FRAGMENT
- import com.adealink.weparty.webview.datasource.local.WebLocalService
- import com.adealink.weparty.webview.jsbridge.JSBridge
- import com.adealink.weparty.webview.jsbridge.data.NativeMessage
- import com.adealink.weparty.webview.jsnativemethod.addCommonMethod
- import com.adealink.weparty.webview.payermax.SpWebChromeClient
- import com.adealink.weparty.webview.payermax.SpWebViewClient
- import com.ushareit.easysdk.web.util.SPWebHelper
- class WebViewFragment : BaseFragment(R.layout.fragment_webview),
- IWebViewCallback {
- @BindExtra(Web.Common.EXTRA_LOADING_URL, desc = "加载图标链接")
- var loadingUrl: String? = null
- @BindExtra(Web.Common.EXTRA_DIALOG_TAG, desc = "dialog的tag")
- var fgTag: String? = ""
- private val binding by viewBinding(FragmentWebviewBinding::bind)
- private var url: String = ""
- private var topRadius: Int = 0
- private var errorComp: ErrorComp? = null
- private var transparentBgWhenPageFinish = false
- private var transparentBg = false
- private var webFragmentCallback: IWebViewFragmentCallback? = null
- private val webHelper by lazy { SPWebHelper() }
- private var isPayerMaxPage = false
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- Router.bind(this)
- }
- override fun initViews() {
- super.initViews()
- val topRadius = arguments?.getInt(EXTRA_TOP_RADIUS) ?: 16.dp()
- binding.webView.outlineProvider = object : ViewOutlineProvider() {
- override fun getOutline(view: View, outline: Outline) {
- val left = 0
- val top = 0
- val right = view.width
- val bottom = view.height
- //top corners
- outline.setRoundRect(
- left,
- top,
- right,
- bottom + topRadius,
- topRadius.toFloat()
- )
- }
- }
- binding.webView.clipToOutline = true
- binding.webView.webViewCallback = this
- if (transparentBg) {
- binding.root.background = null
- binding.webView.setBackgroundColor(0)
- binding.webView.background?.alpha = 0
- }
- initJsBridge()
- initPayerMaxPageIfNeed()
- }
- private fun initJsBridge() {
- binding.webView.getJSBridge(NATIVE_JS_BRIDGE)?.let { bridge ->
- bridge.addCommonMethod(binding.webView)
- }
- }
- private fun initPayerMaxPageIfNeed() {
- val act = activity ?: return
- var host: String? = null
- if (url.isEmpty().not()) {
- host = url.toUri().host
- }
- if (host?.contains("payermax") == true) {
- isPayerMaxPage = true
- }
- val commonWebChromeClient = binding.webView.getCommonWebChromeClient()
- val commonWebViewClient = binding.webView.getCommonWebViewClient()
- val spWebChromeClient = SpWebChromeClient(webHelper, commonWebChromeClient)
- val spWebViewClient = SpWebViewClient(act, binding.webView, commonWebViewClient)
- webHelper.initWebView(
- AppUtil.application,
- act,
- binding.webView,
- spWebChromeClient,
- spWebViewClient
- )
- }
- override fun onRequestPermissionsResult(
- requestCode: Int,
- permissions: Array<out String>,
- grantResults: IntArray
- ) {
- if (isPayerMaxPage) {
- webHelper.onRequestPermissionsResult(requestCode, permissions, grantResults)
- }
- }
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- webHelper.onActivityResult(requestCode, resultCode, data)
- }
- override fun initComponents() {
- super.initComponents()
- errorComp =
- ErrorComp(
- this,
- binding.webView,
- binding.errorView,
- reload = { loadUrl(url) }
- ).apply { attach() }
- }
- override fun onResume() {
- super.onResume()
- notifyResumeMessage()
- }
- private fun notifyResumeMessage() {
- // notifyNativeMessage(NativeMessage<Any>(NativeMessageType.MSG_ON_RESUME.msgType))
- }
- fun showUrl(url: String) {
- this.url = url
- if (isAdded) {
- loadUrl(url)
- }
- }
- fun replaceAndShow(fragmentManager: FragmentManager, contentId: Int, url: String) {
- Log.d(TAG_WEB_FRAGMENT, "replaceAndShow, url:$url")
- this.url = url
- if (isAdded) {
- loadUrl(url)
- return
- }
- fragmentManager.beginTransaction()
- .replace(contentId, this, "WebFragment")
- .commit()
- showUrl(url)
- }
- fun getWebView(): BaseWebView? {
- if (!isAdded) {
- return null
- }
- return binding.webView
- }
- fun setTopRadius(topRadius: Int) {
- this.topRadius = topRadius
- }
- fun setTransparentBgWhenPageFinish() {
- this.transparentBgWhenPageFinish = true
- }
- fun setTransparentBg() {
- this.transparentBg = true
- }
- fun setWebFragmentCallback(callback: IWebViewFragmentCallback?) {
- this.webFragmentCallback = callback
- }
- override fun loadData() {
- super.loadData()
- loadUrl(url)
- }
- private fun loadUrl(url: String) {
- errorComp?.hideError()
- binding.webView.loadUrl(url)
- }
- override val lifeCycleOwner: LifecycleOwner
- get() = viewLifecycleOwner
- override fun closeWebView() {
- webFragmentCallback?.closeWebView()
- }
- override fun setTitle(title: String?) {
- webFragmentCallback?.setTitle(title)
- }
- override fun beforeLoadUrl(url: String) {
- val cookieManager: CookieManager = CookieManager.getInstance()
- cookieManager.setAcceptCookie(true)
- // 设置多个 Cookie
- val cookies = mutableMapOf<String, String>()
- cookies.put(
- "GAMI-web_auth_token",
- AppPref.token
- )
- cookies.put(
- "GAMI-web_app_locale",
- languageManager?.getLanguageCode() ?: ""
- )
- cookies.put(
- "GAMI-cookie_safe_area",
- "${(activity?.statusBarHeightDp() ?: 0)}|${(activity?.naviBarHeightDp() ?: 0)}"
- )
- for (entry in cookies) {
- val cookieString: String = entry.key + "=" + entry.value + "; path=/"
- cookieManager.setCookie(url, cookieString)
- }
- // 同步 Cookie
- cookieManager.flush()
- }
- /**
- * 详见: https://mimolive.sg.larksuite.com/wiki/ZYliwjteIilW5XkmdhtlysIWgGg
- */
- override fun onPageStarted(url: String?, favicon: Bitmap?) {
- binding.progressIndicator.visibility = View.VISIBLE
- loadJSUrl("javascript:window.localStorage.setItem('GAMI-web_auth_token', '${AppPref.token}')")
- loadJSUrl("javascript:window.localStorage.setItem('GAMI-web_app_locale', '${languageManager?.getLanguageCode() ?: ""}')")
- loadJSUrl(
- "javascript:window.GAMI_BRIDGE={" +
- "\"requestHeader\":${requestHeader()}," +
- "\"safeArea\":\"${(activity?.statusBarHeightDp() ?: 0)}|${(activity?.naviBarHeightDp() ?: 0)}\"" +
- "}"
- )
- }
- fun requestHeader(): String {
- val currentTs = System.currentTimeMillis()
- val deviceName = getDeviceName()
- val headers = mutableMapOf<String, String>()
- headers[KEY_TOKEN] = AppPref.token
- headers[KEY_UDID] = App.instance.deviceIdService.getMemOldDeviceId()
- headers[KEY_PACKAGE_NAME] = PackageUtil.getPackageName()
- headers[KEY_DEVICE] = deviceName
- headers[KEY_PLATFORM] = "1"
- headers[KEY_CHANNEL] = "official"
- headers[KEY_API] = "1"
- headers[KEY_VERSION_CODE] = BuildConfig.VERSION_CODE.toString()
- headers[KEY_NETWORK] = "5g"
- headers[KEY_TIME] = currentTs.toString()
- return (toJsonErrorNull(headers) ?: "").also {
- Log.d(TAG_WEB_FRAGMENT, "requestHeader, header:$it")
- }
- }
- override fun onPageFinished(url: String?) {
- binding.progressIndicator.visibility = View.GONE
- WebLocalService.initWebVersion()
- if (transparentBgWhenPageFinish) {
- binding.root.background = null
- binding.webView.setBackgroundColor(0)
- binding.webView.background?.alpha = 0
- }
- }
- override fun onProgressChanged(progress: Int) {
- binding.progressIndicator.setProgressCompat(progress, true)
- }
- override fun onReceivedError(url: String?, errorCode: Int, description: String?) {
- errorComp?.showError("$errorCode:$description")
- }
- override fun onReceivedSslError(handler: SslErrorHandler?, error: SslError?) {
- errorComp?.showError()
- }
- override fun isHostActive(): Boolean {
- val hostActivity = activity ?: return false
- return !hostActivity.isDestroyed && isAdded
- }
- override fun setImmersionMode() {
- webFragmentCallback?.setImmersionMode()
- }
- override fun setNavigationBarMode(mode: NavigationBarMode) {
- webFragmentCallback?.setNavigationBarMode(mode)
- }
- override fun hideNavigationBar() {
- webFragmentCallback?.hideNavigationBar()
- }
- override fun <T> notifyNativeMessage(message: NativeMessage<T>) {
- if (!isViewBindingValid()) {
- return
- }
- binding.webView.notifyNativeMessage(message)
- }
- override fun getJsBridge(name: String): JSBridge? {
- if (!isViewBindingValid()) {
- return null
- }
- return binding.webView.getJSBridge(name)
- }
- override fun url(): String? {
- return url
- }
- override fun onPageLoadSuccess() {
- //loadJSUrl("javascript:window.localStorage.setItem('GAMI-web_auth_token', 'xxxx')")
- }
- private fun loadJSUrl(url: String) {
- loadUrl(url)
- }
- override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
- if (!isViewBindingValid()) {
- return false
- }
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- if (onBackPressed()) {
- return true
- }
- }
- return false
- }
- override fun onBackPressed(): Boolean {
- if (binding.webView.canGoBack()) {
- binding.webView.goBack()
- return true
- }
- return false
- }
- override fun onDestroyView() {
- super.onDestroyView()
- binding.webView.onDestroy()
- }
- }
|