Compare commits
2 Commits
release
...
feature-js
| Author | SHA1 | Date | |
|---|---|---|---|
| 49a610deee | |||
| 61f43d53b2 |
@ -352,9 +352,9 @@ RE.ImageClickListener = function() {
|
|||||||
var img = imgs[i];
|
var img = imgs[i];
|
||||||
var imageClassName = img.className;
|
var imageClassName = img.className;
|
||||||
if (imageClassName == "image-link"|| img.className == "poster") continue;
|
if (imageClassName == "image-link"|| img.className == "poster") continue;
|
||||||
window.imagelistener.imageArr(img.src);
|
window.NativeCallBack.invokeMethod("imageArr", img.src);
|
||||||
img.onclick = function() {
|
img.onclick = function() {
|
||||||
window.imagelistener.imageClick(this.src);
|
window.NativeCallBack.invokeMethod("imageClick", this.src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,8 +43,10 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright (C) 2017 Wasabeef
|
* Copyright (C) 2017 Wasabeef
|
||||||
@ -77,6 +79,8 @@ public class RichEditor extends WebView {
|
|||||||
|
|
||||||
private EmptyCallback mInitialLayoutCallback;
|
private EmptyCallback mInitialLayoutCallback;
|
||||||
|
|
||||||
|
private Map<String, DynamicJsInterface> mDynamicJsInterfaces = new HashMap<>();
|
||||||
|
|
||||||
private String mCurrentContent = "";
|
private String mCurrentContent = "";
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
@ -605,6 +609,14 @@ public class RichEditor extends WebView {
|
|||||||
exec("javascript:RE.formatBlock();");
|
exec("javascript:RE.formatBlock();");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerDynamicJsInterface(String method, DynamicJsInterface jsInterface) {
|
||||||
|
mDynamicJsInterfaces.put(method, jsInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterDynamicJsInterface(String method) {
|
||||||
|
mDynamicJsInterfaces.remove(method);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调用 JS 方法,告诉网页端该 url 对应视频播放的进度
|
* 调用 JS 方法,告诉网页端该 url 对应视频播放的进度
|
||||||
*/
|
*/
|
||||||
@ -862,6 +874,14 @@ public class RichEditor extends WebView {
|
|||||||
public void logMtaEvent(String event) {
|
public void logMtaEvent(String event) {
|
||||||
// do nothing, mta is deprecated
|
// do nothing, mta is deprecated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public void invokeMethod(String method, String data) {
|
||||||
|
DynamicJsInterface jsInterface = mDynamicJsInterfaces.get(method);
|
||||||
|
if (jsInterface != null) {
|
||||||
|
jsInterface.invoke(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -874,4 +894,8 @@ public class RichEditor extends WebView {
|
|||||||
mInitialLayoutCallback = null;
|
mInitialLayoutCallback = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface DynamicJsInterface {
|
||||||
|
void invoke(String data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ class CommunityHomeFragment : LazyFragment() {
|
|||||||
|
|
||||||
override fun onFragmentFirstVisible() {
|
override fun onFragmentFirstVisible() {
|
||||||
|
|
||||||
ArticleWebCacheManager.init(requireContext())
|
ArticleWebCacheManager.init(requireContext().applicationContext)
|
||||||
|
|
||||||
mViewModel = viewModelProvider()
|
mViewModel = viewModelProvider()
|
||||||
|
|
||||||
|
|||||||
@ -8,14 +8,12 @@ import android.widget.TextView
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewpager.widget.ViewPager.LayoutParams
|
|
||||||
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
|
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
|
||||||
import com.gh.common.util.PackageUtils
|
import com.gh.common.util.PackageUtils
|
||||||
import com.gh.gamecenter.R
|
import com.gh.gamecenter.R
|
||||||
import com.gh.gamecenter.adapter.viewholder.UnAvaliableWebviewViewHolder
|
import com.gh.gamecenter.adapter.viewholder.UnAvaliableWebviewViewHolder
|
||||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||||
import com.gh.gamecenter.common.databinding.TabItemBinding
|
import com.gh.gamecenter.common.databinding.TabItemBinding
|
||||||
import com.gh.gamecenter.common.utils.dip2px
|
|
||||||
import com.gh.gamecenter.common.utils.toBinding
|
import com.gh.gamecenter.common.utils.toBinding
|
||||||
import com.gh.gamecenter.common.utils.visibleIf
|
import com.gh.gamecenter.common.utils.visibleIf
|
||||||
import com.gh.gamecenter.core.utils.MtaHelper
|
import com.gh.gamecenter.core.utils.MtaHelper
|
||||||
@ -53,6 +51,7 @@ class ArticleDetailAdapter(
|
|||||||
ITEM_ARTICLE_DETAIL -> {
|
ITEM_ARTICLE_DETAIL -> {
|
||||||
val isWebViewInstalled = PackageUtils.checkWebViewIsAvailable(mContext)
|
val isWebViewInstalled = PackageUtils.checkWebViewIsAvailable(mContext)
|
||||||
if (isWebViewInstalled) {
|
if (isWebViewInstalled) {
|
||||||
|
ArticleDetailContentViewHolder.wvLoadTimeInMills = System.currentTimeMillis()
|
||||||
val binding: ItemArticleDetailContentBinding =
|
val binding: ItemArticleDetailContentBinding =
|
||||||
ItemArticleDetailContentBinding.inflate(mLayoutInflater, parent, false)
|
ItemArticleDetailContentBinding.inflate(mLayoutInflater, parent, false)
|
||||||
ArticleDetailContentViewHolder(binding, mViewModel).apply { articleDetailVH = this }
|
ArticleDetailContentViewHolder(binding, mViewModel).apply { articleDetailVH = this }
|
||||||
|
|||||||
@ -5,10 +5,10 @@ import android.app.Activity
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
|
import android.util.Log
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.webkit.JavascriptInterface
|
import android.webkit.JavascriptInterface
|
||||||
import android.webkit.WebResourceResponse
|
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
@ -42,6 +42,33 @@ class ArticleDetailContentViewHolder(
|
|||||||
var viewModel: ArticleDetailViewModel
|
var viewModel: ArticleDetailViewModel
|
||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var wvLoadTimeInMills = 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
val richEditor = ArticleWebCacheManager
|
||||||
|
.attachToRichEditor(binding.richEditorContainer)
|
||||||
|
.apply {
|
||||||
|
setInputEnabled(false)
|
||||||
|
setPadding(16, 4, 16, 4)
|
||||||
|
enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
|
||||||
|
setTransparentBackground()
|
||||||
|
setLayoutCallback { viewModel.articleRenderedLiveData.postValue(true) }
|
||||||
|
setPageFinishedListener {
|
||||||
|
Log.d("ArticleDetail", "pageFinished->${System.currentTimeMillis() - wvLoadTimeInMills}")
|
||||||
|
viewModel.articlePageFinishedLiveData.postValue(true)
|
||||||
|
}
|
||||||
|
setChromeClientListener(object : RichEditor.WebChromeClientListener {
|
||||||
|
override fun onPageFinished(view: WebView?, url: String?) {
|
||||||
|
Log.d("ArticleDetail", "onPageFinished: $url->${System.currentTimeMillis() - wvLoadTimeInMills}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
|
||||||
|
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "帖子详情")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private var mEntrance = ""
|
private var mEntrance = ""
|
||||||
val articleImgUrlList = ArrayList<String>()
|
val articleImgUrlList = ArrayList<String>()
|
||||||
|
|
||||||
@ -59,11 +86,41 @@ class ArticleDetailContentViewHolder(
|
|||||||
titleTv.setTextColor(R.color.text_title.toColor(binding.root.context))
|
titleTv.setTextColor(R.color.text_title.toColor(binding.root.context))
|
||||||
gameName.setTextColor(R.color.text_subtitleDesc.toColor(binding.root.context))
|
gameName.setTextColor(R.color.text_subtitleDesc.toColor(binding.root.context))
|
||||||
|
|
||||||
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
|
richEditor.registerDynamicJsInterface("imageArr") { url ->
|
||||||
richEditor.setTransparentBackground()
|
val defUrl = url.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
|
||||||
richEditor.setInputEnabled(false)
|
if (!articleImgUrlList.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
|
||||||
richEditor.setPadding(16, 4, 16, 4)
|
articleImgUrlList.add(defUrl)
|
||||||
richEditor.addJavascriptInterface(JsInterface(article.status ?: ""), "imagelistener")
|
}
|
||||||
|
}
|
||||||
|
richEditor.registerDynamicJsInterface("imageClick") { url ->
|
||||||
|
when {
|
||||||
|
url.contains("web_load_dfimg_icon.png") -> {
|
||||||
|
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val status = article.status ?: ""
|
||||||
|
clickToastByStatus(status) {
|
||||||
|
var current = 0
|
||||||
|
var i = 0
|
||||||
|
val size = articleImgUrlList.size
|
||||||
|
while (i < size) {
|
||||||
|
if (url.contains(articleImgUrlList.get(i))) {
|
||||||
|
current = i
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
val intent = ImageViewerActivity.getIntent(
|
||||||
|
binding.root.context, articleImgUrlList, current,
|
||||||
|
mEntrance + "+(帖子详情[" + binding.titleTv.text.toString() + "])"
|
||||||
|
)
|
||||||
|
(binding.root.context as Activity).startActivityForResult(
|
||||||
|
intent,
|
||||||
|
ImageViewerActivity.REQUEST_FOR_VIEWED_IMAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
richEditor.addJavascriptInterface(
|
richEditor.addJavascriptInterface(
|
||||||
OnLinkClickListener(
|
OnLinkClickListener(
|
||||||
root.context,
|
root.context,
|
||||||
@ -73,26 +130,6 @@ class ArticleDetailContentViewHolder(
|
|||||||
"帖子详情"
|
"帖子详情"
|
||||||
), "OnLinkClickListener"
|
), "OnLinkClickListener"
|
||||||
)
|
)
|
||||||
richEditor.setLayoutCallback(object : EmptyCallback {
|
|
||||||
override fun onCallback() {
|
|
||||||
viewModel.articleRenderedLiveData.postValue(true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
richEditor.setPageFinishedListener {
|
|
||||||
viewModel.articlePageFinishedLiveData.postValue(true)
|
|
||||||
}
|
|
||||||
richEditor.setChromeClientListener(object : RichEditor.WebChromeClientListener {
|
|
||||||
override fun onPageFinished(view: WebView?, url: String?) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
|
|
||||||
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "帖子详情")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
richEditor.setWebResourceRequestInterceptor { view, url ->
|
|
||||||
ArticleWebCacheManager.shouldInterceptRequest(view, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
approvalStatusTv.goneIf(article.status == "pass")
|
approvalStatusTv.goneIf(article.status == "pass")
|
||||||
statusContainer.goneIf(article.status == "pass")
|
statusContainer.goneIf(article.status == "pass")
|
||||||
@ -325,7 +362,7 @@ class ArticleDetailContentViewHolder(
|
|||||||
* 回调列表视频播放结束时的时间
|
* 回调列表视频播放结束时的时间
|
||||||
*/
|
*/
|
||||||
fun onVideoPlayedCallback(url: String, position: Int) {
|
fun onVideoPlayedCallback(url: String, position: Int) {
|
||||||
binding.richEditor.onVideoPlayedCallback(url, position)
|
richEditor.onVideoPlayedCallback(url, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateFollowBtn(isFollowed: Boolean) {
|
fun updateFollowBtn(isFollowed: Boolean) {
|
||||||
@ -349,7 +386,7 @@ class ArticleDetailContentViewHolder(
|
|||||||
fun imageClick(url: String) {
|
fun imageClick(url: String) {
|
||||||
when {
|
when {
|
||||||
url.contains("web_load_dfimg_icon.png") -> {
|
url.contains("web_load_dfimg_icon.png") -> {
|
||||||
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() }
|
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
|
||||||
}
|
}
|
||||||
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
|
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
|
||||||
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
|
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
|
||||||
@ -386,4 +423,14 @@ class ArticleDetailContentViewHolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun destroy() {
|
||||||
|
richEditor.setLayoutCallback(null)
|
||||||
|
richEditor.removeJavascriptInterface("imagelistener")
|
||||||
|
richEditor.removeJavascriptInterface("OnLinkClickListener")
|
||||||
|
richEditor.setChromeClientListener(null)
|
||||||
|
richEditor.setContentOwner(false)
|
||||||
|
richEditor.setPageFinishedListener(null)
|
||||||
|
ArticleWebCacheManager.detachFromRichEditor(binding.richEditorContainer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -26,7 +26,6 @@ import com.gh.gamecenter.common.baselist.LoadType
|
|||||||
import com.gh.gamecenter.common.constant.Constants
|
import com.gh.gamecenter.common.constant.Constants
|
||||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||||
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
|
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
|
||||||
import com.gh.gamecenter.feature.entity.CommentEntity
|
|
||||||
import com.gh.gamecenter.common.entity.CommunityEntity
|
import com.gh.gamecenter.common.entity.CommunityEntity
|
||||||
import com.gh.gamecenter.common.entity.NormalShareEntity
|
import com.gh.gamecenter.common.entity.NormalShareEntity
|
||||||
import com.gh.gamecenter.common.eventbus.EBReuse
|
import com.gh.gamecenter.common.eventbus.EBReuse
|
||||||
@ -41,6 +40,7 @@ import com.gh.gamecenter.eventbus.EBDeleteCommentDetail
|
|||||||
import com.gh.gamecenter.eventbus.EBDeleteDetail
|
import com.gh.gamecenter.eventbus.EBDeleteDetail
|
||||||
import com.gh.gamecenter.eventbus.EBTopCommunityChanged
|
import com.gh.gamecenter.eventbus.EBTopCommunityChanged
|
||||||
import com.gh.gamecenter.feature.entity.ArticleDraftEntity
|
import com.gh.gamecenter.feature.entity.ArticleDraftEntity
|
||||||
|
import com.gh.gamecenter.feature.entity.CommentEntity
|
||||||
import com.gh.gamecenter.feature.entity.Permissions
|
import com.gh.gamecenter.feature.entity.Permissions
|
||||||
import com.gh.gamecenter.login.user.UserManager
|
import com.gh.gamecenter.login.user.UserManager
|
||||||
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
|
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
|
||||||
@ -119,11 +119,11 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
|
|||||||
mAdapter?.articleDetailVH?.run {
|
mAdapter?.articleDetailVH?.run {
|
||||||
if (articleImgUrlList.size > 0) {
|
if (articleImgUrlList.size > 0) {
|
||||||
if (imageSet.size == articleImgUrlList.size) {
|
if (imageSet.size == articleImgUrlList.size) {
|
||||||
binding.richEditor.replaceAllDfImage()
|
richEditor.replaceAllDfImage()
|
||||||
} else {
|
} else {
|
||||||
for (i in imageSet) {
|
for (i in imageSet) {
|
||||||
val url = articleImgUrlList[i.toInt()]
|
val url = articleImgUrlList[i.toInt()]
|
||||||
binding.richEditor.replaceDfImageByUrl(url)
|
richEditor.replaceDfImageByUrl(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,6 +165,9 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
|
||||||
|
mAdapter?.articleDetailVH?.destroy()
|
||||||
|
|
||||||
if (mViewModel.detailEntity != null) {
|
if (mViewModel.detailEntity != null) {
|
||||||
HistoryHelper.insertArticleEntity(mViewModel.detailEntity!!)
|
HistoryHelper.insertArticleEntity(mViewModel.detailEntity!!)
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,11 @@ package com.gh.gamecenter.qa.article.detail
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.MutableContextWrapper
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.webkit.WebResourceResponse
|
import android.webkit.WebResourceResponse
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
|
import com.gh.common.view.RichEditor
|
||||||
import com.gh.gamecenter.common.utils.EnvHelper
|
import com.gh.gamecenter.common.utils.EnvHelper
|
||||||
import com.gh.gamecenter.core.utils.MD5Utils
|
import com.gh.gamecenter.core.utils.MD5Utils
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
@ -18,6 +21,7 @@ import retrofit2.http.GET
|
|||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
import retrofit2.http.Streaming
|
import retrofit2.http.Streaming
|
||||||
import retrofit2.http.Url
|
import retrofit2.http.Url
|
||||||
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@ -25,23 +29,26 @@ import java.util.concurrent.TimeUnit
|
|||||||
/**
|
/**
|
||||||
* 社区文章详情-Web资源缓存管理类
|
* 社区文章详情-Web资源缓存管理类
|
||||||
*/
|
*/
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
object ArticleWebCacheManager {
|
object ArticleWebCacheManager {
|
||||||
|
|
||||||
private lateinit var cacheDir: File
|
private lateinit var cacheDir: File
|
||||||
|
|
||||||
|
private lateinit var cacheRichEditor: RichEditor
|
||||||
|
|
||||||
private var isInit = false
|
private var isInit = false
|
||||||
|
|
||||||
fun init(context: Context) {
|
|
||||||
init(File(context.cacheDir, "article/web"))
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
fun init(cacheDir: File) {
|
fun init(applicationContext: Context) {
|
||||||
if (isInit) return
|
if (isInit) return
|
||||||
|
|
||||||
isInit = true
|
isInit = true
|
||||||
|
|
||||||
this.cacheDir = cacheDir.apply {
|
this.cacheRichEditor = RichEditor(
|
||||||
|
MutableContextWrapper(applicationContext)
|
||||||
|
)
|
||||||
|
|
||||||
|
this.cacheDir = File(applicationContext.cacheDir, "article/web").apply {
|
||||||
if (!exists()) mkdirs()
|
if (!exists()) mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +99,25 @@ object ArticleWebCacheManager {
|
|||||||
.subscribe({}, {})
|
.subscribe({}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun attachToRichEditor(parent: ViewGroup): RichEditor {
|
||||||
|
val richEditor = this.cacheRichEditor.apply {
|
||||||
|
(this.context as MutableContextWrapper).baseContext = parent.context
|
||||||
|
setWebResourceRequestInterceptor { view, url -> shouldInterceptRequest(view, url) }
|
||||||
|
}
|
||||||
|
parent.addView(
|
||||||
|
richEditor,
|
||||||
|
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
)
|
||||||
|
return cacheRichEditor
|
||||||
|
}
|
||||||
|
|
||||||
|
fun detachFromRichEditor(parent: ViewGroup) {
|
||||||
|
this.cacheRichEditor.setHtml("", false)
|
||||||
|
this.cacheRichEditor.setWebResourceRequestInterceptor(null)
|
||||||
|
(this.cacheRichEditor.context as MutableContextWrapper).baseContext = parent.context.applicationContext
|
||||||
|
parent.removeView(this.cacheRichEditor)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拦截WebView的资源请求,并返回URL对应的缓存数据
|
* 拦截WebView的资源请求,并返回URL对应的缓存数据
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -108,11 +108,11 @@ class NewQuestionDetailFragment :
|
|||||||
mAdapter?.questionDetailVH?.run {
|
mAdapter?.questionDetailVH?.run {
|
||||||
if (questionImgUrlList.size > 0) {
|
if (questionImgUrlList.size > 0) {
|
||||||
if (imageSet.size == questionImgUrlList.size) {
|
if (imageSet.size == questionImgUrlList.size) {
|
||||||
binding.richEditor.replaceAllDfImage()
|
richEditor.replaceAllDfImage()
|
||||||
} else {
|
} else {
|
||||||
for (i in imageSet) {
|
for (i in imageSet) {
|
||||||
val url = questionImgUrlList[i.toInt()]
|
val url = questionImgUrlList[i.toInt()]
|
||||||
binding.richEditor.replaceDfImageByUrl(url)
|
richEditor.replaceDfImageByUrl(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -742,6 +742,11 @@ class NewQuestionDetailFragment :
|
|||||||
mBinding.root.setBackgroundColor(Color.TRANSPARENT)
|
mBinding.root.setBackgroundColor(Color.TRANSPARENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
mAdapter?.questionDetailVH?.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDarkModeChanged() {
|
override fun onDarkModeChanged() {
|
||||||
super.onDarkModeChanged()
|
super.onDarkModeChanged()
|
||||||
mAdapter?.let { it.notifyItemRangeChanged(0, it.itemCount) }
|
mAdapter?.let { it.notifyItemRangeChanged(0, it.itemCount) }
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import android.text.SpannableStringBuilder
|
|||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.webkit.JavascriptInterface
|
import android.webkit.JavascriptInterface
|
||||||
import android.webkit.WebResourceResponse
|
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
@ -38,43 +37,39 @@ class QuestionDetailContentViewHolder(
|
|||||||
var viewModel: NewQuestionDetailViewModel
|
var viewModel: NewQuestionDetailViewModel
|
||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
val richEditor = ArticleWebCacheManager
|
||||||
|
.attachToRichEditor(binding.richEditorContainer)
|
||||||
|
.apply {
|
||||||
|
setInputEnabled(false)
|
||||||
|
setPadding(16, 4, 16, 4)
|
||||||
|
enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
|
||||||
|
setTransparentBackground()
|
||||||
|
setLayoutCallback { viewModel.questionRenderedLiveData.postValue(true) }
|
||||||
|
setPageFinishedListener { viewModel.questionPageFinishedLiveData.postValue(true) }
|
||||||
|
setChromeClientListener(object : RichEditor.WebChromeClientListener {
|
||||||
|
override fun onPageFinished(view: WebView?, url: String?) {}
|
||||||
|
|
||||||
|
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
|
||||||
|
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "问题详情")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private var mEntrance = ""
|
private var mEntrance = ""
|
||||||
val questionImgUrlList = ArrayList<String>()
|
val questionImgUrlList = ArrayList<String>()
|
||||||
|
|
||||||
@SuppressLint("AddJavascriptInterface")
|
@SuppressLint("AddJavascriptInterface")
|
||||||
fun bindView(question: QuestionsDetailEntity) {
|
fun bindView(question: QuestionsDetailEntity) {
|
||||||
binding.run {
|
binding.run {
|
||||||
richEditor.setInputEnabled(false)
|
richEditor.removeJavascriptInterface("imagelistener")
|
||||||
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
|
|
||||||
richEditor.setTransparentBackground()
|
|
||||||
richEditor.setPadding(16, 4, 16, 4)
|
|
||||||
richEditor.addJavascriptInterface(JsInterface(question.status), "imagelistener")
|
richEditor.addJavascriptInterface(JsInterface(question.status), "imagelistener")
|
||||||
|
richEditor.removeJavascriptInterface("OnLinkClickListener")
|
||||||
richEditor.addJavascriptInterface(
|
richEditor.addJavascriptInterface(
|
||||||
OnLinkClickListener(
|
OnLinkClickListener(
|
||||||
root.context, question.title
|
root.context, question.title
|
||||||
?: "", question.status, mEntrance, "问题详情"
|
?: "", question.status, mEntrance, "问题详情"
|
||||||
), "OnLinkClickListener"
|
), "OnLinkClickListener"
|
||||||
)
|
)
|
||||||
richEditor.setLayoutCallback(object : EmptyCallback {
|
|
||||||
override fun onCallback() {
|
|
||||||
viewModel.questionRenderedLiveData.postValue(true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
richEditor.setPageFinishedListener {
|
|
||||||
viewModel.questionPageFinishedLiveData.postValue(true)
|
|
||||||
}
|
|
||||||
richEditor.setChromeClientListener(object : RichEditor.WebChromeClientListener {
|
|
||||||
override fun onPageFinished(view: WebView?, url: String?) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
|
|
||||||
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "问题详情")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
richEditor.setWebResourceRequestInterceptor { view, url ->
|
|
||||||
ArticleWebCacheManager.shouldInterceptRequest(view, url)
|
|
||||||
}
|
|
||||||
approvalStatusTv.goneIf(question.status == "pass")
|
approvalStatusTv.goneIf(question.status == "pass")
|
||||||
statusContainer.goneIf(question.status == "pass")
|
statusContainer.goneIf(question.status == "pass")
|
||||||
when (question.status) {
|
when (question.status) {
|
||||||
@ -280,7 +275,7 @@ class QuestionDetailContentViewHolder(
|
|||||||
* 回调列表视频播放结束时的时间
|
* 回调列表视频播放结束时的时间
|
||||||
*/
|
*/
|
||||||
fun onVideoPlayedCallback(url: String, position: Int) {
|
fun onVideoPlayedCallback(url: String, position: Int) {
|
||||||
binding.richEditor.onVideoPlayedCallback(url, position)
|
richEditor.onVideoPlayedCallback(url, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class JsInterface(val status: String) {
|
inner class JsInterface(val status: String) {
|
||||||
@ -288,7 +283,7 @@ class QuestionDetailContentViewHolder(
|
|||||||
fun imageClick(url: String) {
|
fun imageClick(url: String) {
|
||||||
when {
|
when {
|
||||||
url.contains("web_load_dfimg_icon.png") -> {
|
url.contains("web_load_dfimg_icon.png") -> {
|
||||||
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() }
|
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
|
||||||
}
|
}
|
||||||
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
|
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
|
||||||
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
|
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
|
||||||
@ -325,4 +320,14 @@ class QuestionDetailContentViewHolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun destroy() {
|
||||||
|
richEditor.setLayoutCallback(null)
|
||||||
|
richEditor.removeJavascriptInterface("imagelistener")
|
||||||
|
richEditor.removeJavascriptInterface("OnLinkClickListener")
|
||||||
|
richEditor.setChromeClientListener(null)
|
||||||
|
richEditor.setContentOwner(false)
|
||||||
|
richEditor.setPageFinishedListener(null)
|
||||||
|
ArticleWebCacheManager.detachFromRichEditor(binding.richEditorContainer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -217,8 +217,8 @@
|
|||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="这是一个很长很长很长很长很长很长很长很长很长很长的标题" />
|
tools:text="这是一个很长很长很长很长很长很长很长很长很长很长的标题" />
|
||||||
|
|
||||||
<com.gh.common.view.RichEditor
|
<FrameLayout
|
||||||
android:id="@+id/richEditor"
|
android:id="@+id/richEditorContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="13dp" />
|
android:layout_marginBottom="13dp" />
|
||||||
|
|||||||
Reference in New Issue
Block a user