feat: 论坛文章详情WebView预加载
This commit is contained in:
@ -128,6 +128,10 @@ public class RichEditor extends WebView {
|
||||
private WebResourceRequestInterceptor mWebResourceRequestInterceptor;
|
||||
private PageFinishedListener mPageFinishedListener;
|
||||
|
||||
private OnLinkClickListener mOnLinkClickListener;
|
||||
|
||||
private ImageListener mImageListener;
|
||||
|
||||
public RichEditor(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@ -146,6 +150,8 @@ public class RichEditor extends WebView {
|
||||
ExtensionsKt.fixUiModeIfNeeded(this);
|
||||
|
||||
addJavascriptInterface(new NativeCallBack(), "NativeCallBack");
|
||||
addJavascriptInterface(new LinkClickNativeCallback(), "OnLinkClickListener");
|
||||
addJavascriptInterface(new ImageNativeCallback(), "imagelistener");
|
||||
|
||||
setVerticalScrollBarEnabled(false);
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
@ -195,6 +201,14 @@ public class RichEditor extends WebView {
|
||||
mInitialLayoutCallback = layoutCallback;
|
||||
}
|
||||
|
||||
public void setOnLinkClickListener(OnLinkClickListener onLinkClickListener) {
|
||||
this.mOnLinkClickListener = onLinkClickListener;
|
||||
}
|
||||
|
||||
public void setImageListener(ImageListener imageListener) {
|
||||
this.mImageListener = imageListener;
|
||||
}
|
||||
|
||||
private void callback(String text) {
|
||||
mContents = text.replaceFirst(CALLBACK_SCHEME, "");
|
||||
if (mTextChangeListener != null) {
|
||||
@ -864,6 +878,96 @@ public class RichEditor extends WebView {
|
||||
}
|
||||
}
|
||||
|
||||
class LinkClickNativeCallback {
|
||||
|
||||
/**
|
||||
* 链接点击回调
|
||||
* @param content 回调内容
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void onClick(String content) {
|
||||
if (mOnLinkClickListener != null) {
|
||||
mOnLinkClickListener.onClick(content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频点击回调
|
||||
* @param content 回调内容
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void onVideoClick(String content) {
|
||||
if (mOnLinkClickListener != null) {
|
||||
mOnLinkClickListener.onVideoClick(content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频点击回调
|
||||
* @param url 视频链接
|
||||
* @param poster 视频封面
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void onVideoClick(String url, String poster) {
|
||||
if (mOnLinkClickListener != null) {
|
||||
mOnLinkClickListener.onVideoClick(url, poster);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频点击回调
|
||||
* @param url 视频链接
|
||||
* @param poster 视频封面
|
||||
* @param position 视频下标位置
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void onVideoClick(String url, String poster, long position) {
|
||||
if (mOnLinkClickListener != null) {
|
||||
mOnLinkClickListener.onVideoClick(url, poster, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImageNativeCallback {
|
||||
/**
|
||||
* 图片点击回调
|
||||
* @param url 图片链接
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void imageClick(String url) {
|
||||
if (mImageListener != null) {
|
||||
mImageListener.onImageClick(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片加载回调
|
||||
* @param url 图片链接
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void imageArr(String url) {
|
||||
if (mImageListener != null) {
|
||||
mImageListener.onImageLoad(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnLinkClickListener {
|
||||
void onClick(String content);
|
||||
|
||||
void onVideoClick(String content);
|
||||
|
||||
void onVideoClick(String url, String poster);
|
||||
|
||||
void onVideoClick(String url, String poster, long position);
|
||||
}
|
||||
|
||||
public interface ImageListener {
|
||||
void onImageLoad(String url);
|
||||
|
||||
void onImageClick(String url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
super.invalidate();
|
||||
|
||||
@ -32,7 +32,7 @@ import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.entity.ForumVideoEntity
|
||||
import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity
|
||||
import com.gh.gamecenter.fragment.MainWrapperFragment
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleWebCacheManager
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailWebCacheManager
|
||||
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
|
||||
import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity
|
||||
import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
|
||||
@ -62,7 +62,7 @@ class CommunityHomeFragment : LazyFragment() {
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
|
||||
ArticleWebCacheManager.init(requireContext())
|
||||
ArticleDetailWebCacheManager.init(requireContext().applicationContext)
|
||||
|
||||
mViewModel = viewModelProvider()
|
||||
|
||||
|
||||
@ -255,8 +255,8 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
|
||||
mRichEditor = viewHolder.binding.newsdetailItemWvContent;
|
||||
if (viewHolder.binding.newsdetailItemWvContent.getTag() == null) {
|
||||
viewHolder.binding.newsdetailItemWvContent.addJavascriptInterface(new OnLinkClickListener(mContext, mEntrance, "", mNewsDetailEntity.getTitle(), "新闻详情"), "OnLinkClickListener");
|
||||
viewHolder.binding.newsdetailItemWvContent.addJavascriptInterface(new JsInterface(viewHolder.binding.newsdetailItemWvContent), "imagelistener");
|
||||
viewHolder.binding.newsdetailItemWvContent.setOnLinkClickListener(new OnLinkClickListener(mContext, mEntrance, "", mNewsDetailEntity.getTitle(), "新闻详情"));
|
||||
viewHolder.binding.newsdetailItemWvContent.setImageListener(new ImageListener(viewHolder.binding.newsdetailItemWvContent));
|
||||
mWebSettings = viewHolder.binding.newsdetailItemWvContent.getSettings();
|
||||
mWebSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
@ -776,15 +776,15 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
}
|
||||
}
|
||||
|
||||
public class JsInterface {
|
||||
public class ImageListener implements RichEditor.ImageListener {
|
||||
private RichEditor editor;
|
||||
|
||||
public JsInterface(RichEditor editor) {
|
||||
public ImageListener(RichEditor editor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void imageClick(String url) {
|
||||
@Override
|
||||
public void onImageClick(String url) {
|
||||
if (url.contains("web_load_dfimg_icon.png")) {
|
||||
editor.post(() -> editor.replaceAllDfImageExcludeGif());
|
||||
} else {
|
||||
@ -801,8 +801,8 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void imageArr(String url) {
|
||||
@Override
|
||||
public void onImageLoad(String url) {
|
||||
String defUrl = url.split("\\?")[0];
|
||||
//web_load_dfimg_icon.png 查看大图按钮
|
||||
if (!mNewsImgs.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
|
||||
|
||||
@ -124,10 +124,9 @@ open class AnswerDetailFragment : ToolbarFragment() {
|
||||
|
||||
@SuppressLint("AddJavascriptInterface")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
mBinding.richEditor.addJavascriptInterface(JsInterface(), "imagelistener")
|
||||
mBinding.richEditor.addJavascriptInterface(
|
||||
OnLinkClickListener(requireContext(), "", "", mEntrance, "回答详情"),
|
||||
"OnLinkClickListener"
|
||||
mBinding.richEditor.setImageListener(ImageListener())
|
||||
mBinding.richEditor.setOnLinkClickListener(
|
||||
OnLinkClickListener(requireContext(), "", "", mEntrance, "回答详情")
|
||||
)
|
||||
mSkeletonScreen =
|
||||
Skeleton.bind(mBinding.skeletonMask).load(R.layout.fragment_answer_detail_skeleton).shimmer(false).show()
|
||||
@ -1256,9 +1255,9 @@ open class AnswerDetailFragment : ToolbarFragment() {
|
||||
dialog.setContentView(viewDialog)
|
||||
}
|
||||
|
||||
inner class JsInterface {
|
||||
@JavascriptInterface
|
||||
fun imageClick(url: String) {
|
||||
inner class ImageListener : RichEditor.ImageListener {
|
||||
|
||||
override fun onImageClick(url: String) {
|
||||
when {
|
||||
url.contains("web_load_dfimg_icon.png") -> mBaseHandler.post { mBinding.richEditor.replaceAllDfImageExcludeGif() }
|
||||
//url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> mBaseHandler.post { mBinding.richEditor.replaceDfImageByUrl(url) }
|
||||
@ -1281,8 +1280,8 @@ open class AnswerDetailFragment : ToolbarFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun imageArr(url: String) {
|
||||
|
||||
override fun onImageLoad(url: String) {
|
||||
val defUrl = url.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
|
||||
if (!mAnswersImages!!.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
|
||||
mAnswersImages.add(defUrl)
|
||||
|
||||
@ -8,14 +8,12 @@ import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager.widget.ViewPager.LayoutParams
|
||||
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.UnAvaliableWebviewViewHolder
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
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.visibleIf
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
|
||||
@ -7,8 +7,6 @@ import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
@ -42,6 +40,26 @@ class ArticleDetailContentViewHolder(
|
||||
var viewModel: ArticleDetailViewModel
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
val richEditor = ArticleDetailWebCacheManager
|
||||
.attachToRichEditor(binding.richEditorContainer)
|
||||
.apply {
|
||||
setInputEnabled(false)
|
||||
setPadding(16, 4, 16, 4)
|
||||
enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
|
||||
setTransparentBackground()
|
||||
setLayoutCallback { viewModel.articleRenderedLiveData.postValue(true) }
|
||||
setPageFinishedListener {
|
||||
viewModel.articlePageFinishedLiveData.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 = ""
|
||||
val articleImgUrlList = ArrayList<String>()
|
||||
|
||||
@ -59,41 +77,6 @@ class ArticleDetailContentViewHolder(
|
||||
titleTv.setTextColor(R.color.text_title.toColor(binding.root.context))
|
||||
gameName.setTextColor(R.color.text_subtitleDesc.toColor(binding.root.context))
|
||||
|
||||
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
|
||||
richEditor.setTransparentBackground()
|
||||
richEditor.setInputEnabled(false)
|
||||
richEditor.setPadding(16, 4, 16, 4)
|
||||
richEditor.addJavascriptInterface(JsInterface(article.status ?: ""), "imagelistener")
|
||||
richEditor.addJavascriptInterface(
|
||||
OnLinkClickListener(
|
||||
root.context,
|
||||
article.title,
|
||||
article.status ?: "",
|
||||
mEntrance,
|
||||
"帖子详情"
|
||||
), "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")
|
||||
statusContainer.goneIf(article.status == "pass")
|
||||
when (article.status) {
|
||||
@ -186,6 +169,16 @@ class ArticleDetailContentViewHolder(
|
||||
userNameTv.text = article.user.name
|
||||
userIconIv.display(article.user.border, article.user.icon, article.user.auth?.icon)
|
||||
richEditor.setContentOwner(article.me.isContentOwner)
|
||||
richEditor.setImageListener(ImageListener(article.status ?: ""))
|
||||
richEditor.setOnLinkClickListener(
|
||||
OnLinkClickListener(
|
||||
root.context,
|
||||
article.title,
|
||||
article.status ?: "",
|
||||
mEntrance,
|
||||
"帖子详情"
|
||||
)
|
||||
)
|
||||
// 避免列表频繁刷新
|
||||
if (richEditor.currentContent != article.content) {
|
||||
richEditor.setHtml(article.content, true)
|
||||
@ -325,7 +318,7 @@ class ArticleDetailContentViewHolder(
|
||||
* 回调列表视频播放结束时的时间
|
||||
*/
|
||||
fun onVideoPlayedCallback(url: String, position: Int) {
|
||||
binding.richEditor.onVideoPlayedCallback(url, position)
|
||||
richEditor.onVideoPlayedCallback(url, position)
|
||||
}
|
||||
|
||||
fun updateFollowBtn(isFollowed: Boolean) {
|
||||
@ -344,12 +337,12 @@ class ArticleDetailContentViewHolder(
|
||||
}
|
||||
|
||||
|
||||
inner class JsInterface(val status: String) {
|
||||
@JavascriptInterface
|
||||
fun imageClick(url: String) {
|
||||
inner class ImageListener(val status: String) : RichEditor.ImageListener {
|
||||
|
||||
override fun onImageClick(url: String) {
|
||||
when {
|
||||
url.contains("web_load_dfimg_icon.png") -> {
|
||||
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() }
|
||||
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
|
||||
}
|
||||
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
|
||||
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
|
||||
@ -378,12 +371,21 @@ class ArticleDetailContentViewHolder(
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun imageArr(url: String) {
|
||||
override fun onImageLoad(url: String) {
|
||||
val defUrl = url.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
|
||||
if (!articleImgUrlList.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
|
||||
articleImgUrlList.add(defUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
richEditor.setLayoutCallback(null)
|
||||
richEditor.setImageListener(null)
|
||||
richEditor.setOnLinkClickListener(null)
|
||||
richEditor.setChromeClientListener(null)
|
||||
richEditor.setContentOwner(false)
|
||||
richEditor.setPageFinishedListener(null)
|
||||
ArticleDetailWebCacheManager.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.EntranceConsts
|
||||
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.NormalShareEntity
|
||||
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.EBTopCommunityChanged
|
||||
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.login.user.UserManager
|
||||
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
|
||||
@ -119,11 +119,11 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
|
||||
mAdapter?.articleDetailVH?.run {
|
||||
if (articleImgUrlList.size > 0) {
|
||||
if (imageSet.size == articleImgUrlList.size) {
|
||||
binding.richEditor.replaceAllDfImage()
|
||||
richEditor.replaceAllDfImage()
|
||||
} else {
|
||||
for (i in imageSet) {
|
||||
val url = articleImgUrlList[i.toInt()]
|
||||
binding.richEditor.replaceDfImageByUrl(url)
|
||||
richEditor.replaceDfImageByUrl(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,6 +165,9 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
|
||||
mAdapter?.articleDetailVH?.destroy()
|
||||
|
||||
if (mViewModel.detailEntity != null) {
|
||||
HistoryHelper.insertArticleEntity(mViewModel.detailEntity!!)
|
||||
|
||||
|
||||
@ -2,8 +2,11 @@ package com.gh.gamecenter.qa.article.detail
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.MutableContextWrapper
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import com.gh.common.view.RichEditor
|
||||
import com.gh.gamecenter.common.utils.EnvHelper
|
||||
import com.gh.gamecenter.core.utils.MD5Utils
|
||||
import io.reactivex.Single
|
||||
@ -25,23 +28,26 @@ import java.util.concurrent.TimeUnit
|
||||
/**
|
||||
* 社区文章详情-Web资源缓存管理类
|
||||
*/
|
||||
object ArticleWebCacheManager {
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object ArticleDetailWebCacheManager {
|
||||
|
||||
private lateinit var cacheDir: File
|
||||
|
||||
private lateinit var cacheRichEditor: RichEditor
|
||||
|
||||
private var isInit = false
|
||||
|
||||
fun init(context: Context) {
|
||||
init(File(context.cacheDir, "article/web"))
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun init(cacheDir: File) {
|
||||
fun init(applicationContext: Context) {
|
||||
if (isInit) return
|
||||
|
||||
isInit = true
|
||||
|
||||
this.cacheDir = cacheDir.apply {
|
||||
this.cacheRichEditor = RichEditor(
|
||||
MutableContextWrapper(applicationContext)
|
||||
)
|
||||
|
||||
this.cacheDir = File(applicationContext.cacheDir, "article/web").apply {
|
||||
if (!exists()) mkdirs()
|
||||
}
|
||||
|
||||
@ -92,10 +98,29 @@ object ArticleWebCacheManager {
|
||||
.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("", true)
|
||||
this.cacheRichEditor.setWebResourceRequestInterceptor(null)
|
||||
(this.cacheRichEditor.context as MutableContextWrapper).baseContext = parent.context.applicationContext
|
||||
parent.removeView(this.cacheRichEditor)
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截WebView的资源请求,并返回URL对应的缓存数据
|
||||
*/
|
||||
fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
|
||||
private fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
|
||||
val urlWithoutQueryParams = url.split("?")[0]
|
||||
|
||||
val cacheFile = File(cacheDir, MD5Utils.getUrlMD5(urlWithoutQueryParams))
|
||||
@ -5,6 +5,7 @@ import android.webkit.JavascriptInterface
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.view.RichEditor
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.core.utils.GsonUtils
|
||||
import com.gh.gamecenter.common.utils.clickToastByStatus
|
||||
@ -25,9 +26,9 @@ class OnLinkClickListener(
|
||||
val status: String = "",
|
||||
val entrance: String,
|
||||
val path: String,
|
||||
) {
|
||||
@JavascriptInterface
|
||||
fun onClick(content: String) {
|
||||
) : RichEditor.OnLinkClickListener {
|
||||
|
||||
override fun onClick(content: String) {
|
||||
AppExecutor.uiExecutor.execute {
|
||||
tryWithDefaultCatch {
|
||||
val insertEntity = GsonUtils.fromJson(content, EditorInsertEntity::class.java)
|
||||
@ -70,8 +71,7 @@ class OnLinkClickListener(
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun onVideoClick(content: String) {
|
||||
override fun onVideoClick(content: String) {
|
||||
AppExecutor.uiExecutor.execute {
|
||||
tryWithDefaultCatch {
|
||||
val videoEntity = GsonUtils.fromJson(content, MyVideoEntity::class.java)
|
||||
@ -82,15 +82,13 @@ class OnLinkClickListener(
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun onVideoClick(url: String, poster: String) {
|
||||
override fun onVideoClick(url: String, poster: String) {
|
||||
clickToastByStatus(status) {
|
||||
FullScreenVideoActivity.start(context, title, url, poster)
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun onVideoClick(url: String, poster: String, position: Long) {
|
||||
override fun onVideoClick(url: String, poster: String, position: Long) {
|
||||
clickToastByStatus(status) {
|
||||
FullScreenVideoActivity.start(context, title, url, poster, position)
|
||||
}
|
||||
|
||||
@ -108,11 +108,11 @@ class NewQuestionDetailFragment :
|
||||
mAdapter?.questionDetailVH?.run {
|
||||
if (questionImgUrlList.size > 0) {
|
||||
if (imageSet.size == questionImgUrlList.size) {
|
||||
binding.richEditor.replaceAllDfImage()
|
||||
richEditor.replaceAllDfImage()
|
||||
} else {
|
||||
for (i in imageSet) {
|
||||
val url = questionImgUrlList[i.toInt()]
|
||||
binding.richEditor.replaceDfImageByUrl(url)
|
||||
richEditor.replaceDfImageByUrl(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -742,6 +742,11 @@ class NewQuestionDetailFragment :
|
||||
mBinding.root.setBackgroundColor(Color.TRANSPARENT)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
mAdapter?.questionDetailVH?.destroy()
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
mAdapter?.let { it.notifyItemRangeChanged(0, it.itemCount) }
|
||||
|
||||
@ -5,8 +5,6 @@ import android.app.Activity
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.view.View
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -27,7 +25,7 @@ import com.gh.gamecenter.core.runOnUiThread
|
||||
import com.gh.gamecenter.core.utils.*
|
||||
import com.gh.gamecenter.databinding.ItemArticleDetailContentBinding
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleWebCacheManager
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailWebCacheManager
|
||||
import com.gh.gamecenter.qa.article.detail.EditHistoryDialog
|
||||
import com.gh.gamecenter.qa.editor.OnLinkClickListener
|
||||
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
|
||||
@ -38,43 +36,30 @@ class QuestionDetailContentViewHolder(
|
||||
var viewModel: NewQuestionDetailViewModel
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
val richEditor = ArticleDetailWebCacheManager
|
||||
.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 = ""
|
||||
val questionImgUrlList = ArrayList<String>()
|
||||
|
||||
@SuppressLint("AddJavascriptInterface")
|
||||
fun bindView(question: QuestionsDetailEntity) {
|
||||
binding.run {
|
||||
richEditor.setInputEnabled(false)
|
||||
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
|
||||
richEditor.setTransparentBackground()
|
||||
richEditor.setPadding(16, 4, 16, 4)
|
||||
richEditor.addJavascriptInterface(JsInterface(question.status), "imagelistener")
|
||||
richEditor.addJavascriptInterface(
|
||||
OnLinkClickListener(
|
||||
root.context, question.title
|
||||
?: "", question.status, mEntrance, "问题详情"
|
||||
), "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")
|
||||
statusContainer.goneIf(question.status == "pass")
|
||||
when (question.status) {
|
||||
@ -156,6 +141,16 @@ class QuestionDetailContentViewHolder(
|
||||
userNameTv.text = question.user.name
|
||||
userIconIv.display(question.user.border, question.user.icon, question.user.auth?.icon)
|
||||
richEditor.setContentOwner(question.me.isContentOwner)
|
||||
richEditor.setImageListener(ImageListener(question.status))
|
||||
richEditor.setOnLinkClickListener(
|
||||
OnLinkClickListener(
|
||||
root.context,
|
||||
question.title ?: "",
|
||||
question.status,
|
||||
mEntrance,
|
||||
"问题详情"
|
||||
)
|
||||
)
|
||||
// 避免列表频繁刷新
|
||||
if (richEditor.currentContent != question.description) {
|
||||
richEditor.setHtml(question.description, true)
|
||||
@ -280,15 +275,15 @@ class QuestionDetailContentViewHolder(
|
||||
* 回调列表视频播放结束时的时间
|
||||
*/
|
||||
fun onVideoPlayedCallback(url: String, position: Int) {
|
||||
binding.richEditor.onVideoPlayedCallback(url, position)
|
||||
richEditor.onVideoPlayedCallback(url, position)
|
||||
}
|
||||
|
||||
inner class JsInterface(val status: String) {
|
||||
@JavascriptInterface
|
||||
fun imageClick(url: String) {
|
||||
inner class ImageListener(val status: String) : RichEditor.ImageListener {
|
||||
|
||||
override fun onImageClick(url: String) {
|
||||
when {
|
||||
url.contains("web_load_dfimg_icon.png") -> {
|
||||
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() }
|
||||
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
|
||||
}
|
||||
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
|
||||
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
|
||||
@ -317,12 +312,21 @@ class QuestionDetailContentViewHolder(
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun imageArr(url: String) {
|
||||
override fun onImageLoad(url: String) {
|
||||
val defUrl = url.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
|
||||
if (!questionImgUrlList.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
|
||||
questionImgUrlList.add(defUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
richEditor.setLayoutCallback(null)
|
||||
richEditor.setImageListener(null)
|
||||
richEditor.setOnLinkClickListener(null)
|
||||
richEditor.setChromeClientListener(null)
|
||||
richEditor.setContentOwner(false)
|
||||
richEditor.setPageFinishedListener(null)
|
||||
ArticleDetailWebCacheManager.detachFromRichEditor(binding.richEditorContainer)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user