package com.gh.base import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.app.Activity import android.content.ClipboardManager import android.content.Context import android.content.Intent import android.graphics.Color import android.os.Bundle import android.text.TextUtils import android.view.View import android.webkit.JavascriptInterface import android.widget.CheckBox import android.widget.FrameLayout import android.widget.TextView import androidx.lifecycle.Observer import com.gh.common.util.DialogUtils import com.gh.common.util.NewLogUtils import com.gh.common.view.RichEditor import com.gh.gamecenter.CropImageActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.base.activity.ToolBarActivity import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.* import com.gh.gamecenter.entity.* import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.qa.editor.* import com.gh.gamecenter.feature.entity.AnswerEntity import com.gh.gamecenter.feature.entity.ArticleEntity import com.gh.gamecenter.qa.entity.EditorInsertEntity import com.gh.gamecenter.video.poster.PosterEditActivity import com.gh.gamecenter.video.upload.UploadManager import com.google.gson.JsonObject import com.halo.assistant.HaloApp import com.lightgame.utils.Util_System_Keyboard import com.lightgame.utils.Utils import com.lightgame.view.CheckableImageView import io.reactivex.disposables.Disposable import org.json.JSONArray import org.json.JSONObject import java.io.File // TODO: 移动到module_bbs模块 abstract class BaseRichEditorActivity constructor() : ToolBarActivity(), KeyboardHeightObserver, UploadVideoListener { lateinit var mRichEditor: RichEditor private lateinit var mEditorTextNumTv: TextView private lateinit var mEditorFont: CheckableImageView private lateinit var mEditorLink: CheckableImageView private lateinit var mEditorFontBold: CheckableImageView private lateinit var mEditorFontItalic: CheckableImageView private lateinit var mEditorFontStrikeThrough: CheckableImageView private lateinit var mEditorFontUnderline: CheckableImageView private lateinit var mEditorParagraphDivider: CheckableImageView private lateinit var mEditorParagraphH1: CheckableImageView private lateinit var mEditorParagraphH2: CheckableImageView private lateinit var mEditorParagraphH3: CheckableImageView private lateinit var mEditorParagraphH4: CheckableImageView private lateinit var mEditorParagraphQuote: CheckableImageView private lateinit var mEditorAlignLeft: CheckableImageView private lateinit var mEditorAlignCenter: CheckableImageView private lateinit var mEditorAlignRight: CheckableImageView private lateinit var mEditorAlignContainer: View private lateinit var mEditorFontContainer: View private lateinit var mEditorParagraphContainer: View private lateinit var mEditorLinkContainer: View private lateinit var mEditorInsertDetailContainer: View private lateinit var mTagsContainer: FrameLayout private lateinit var mUploadVideoGuideContainer: View protected lateinit var mOriginalCb: CheckBox private lateinit var mOriginalTipsContainer: View private lateinit var mOriginalTipsClose: TextView private var mCurrentParagraphStyle = "" private var mIsExtendedKeyboardShow = false private var mAgreePostPic: Boolean = false private var mGuideDisposable: Disposable? = null protected lateinit var mViewModel: VM protected var mIsKeyBoardShow = false private var mKeyboardHeightProvider: KeyboardHeightProvider? = null private var mMaxUploadVideoGuideCount = 2 val FILE_HOST = "file:///" override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) DialogUtils.fixWebViewKeyboardNotWorking(this) if (resultCode != Activity.RESULT_OK) return val insertData: EditorInsertEntity? when (requestCode) { INSERT_ANSWER_CODE -> { val answer = data?.getParcelableExtra(AnswerEntity::class.java.simpleName) if (answer != null) { mRichEditor.focusEditor() insertData = EditorInsertEntity.transform(answer) mRichEditor.insertCustomStyleLink(insertData) } } INSERT_ARTICLE_CODE -> { val article = data?.getParcelableExtra(ArticleEntity::class.java.simpleName) if (article != null) { mRichEditor.focusEditor() insertData = EditorInsertEntity.transform(article) mRichEditor.insertCustomStyleLink(insertData) } } INSERT_GAME_CODE -> { val game = data?.getParcelableExtra(GameEntity::class.java.simpleName) if (game != null) { mRichEditor.focusEditor() insertData = EditorInsertEntity.transform(game) mRichEditor.insertCustomStyleLink(insertData) } } INSERT_GAME_COLLECTION_CODE -> { val gameCollectionEntity = data?.getParcelableExtra(GamesCollectionEntity::class.java.simpleName) if (gameCollectionEntity != null) { mRichEditor.focusEditor() insertData = EditorInsertEntity.transform(gameCollectionEntity) mRichEditor.insertCustomStyleLink(insertData) } } REQUEST_CODE_IMAGE -> { if (data != null) mViewModel.uploadPic(data) } INSERT_MEDIA_VIDEO_CODE -> { val localVideoList = data?.getParcelableArrayListExtra(LocalVideoEntity::class.java.name) ?: arrayListOf() if (localVideoList.isNotEmpty()) { mRichEditor.focusEditor() uploadVideo(localVideoList) } } REQUEST_CODE_IMAGE_CROP -> { val imagePath = data?.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) if (!imagePath.isNullOrEmpty()) { mViewModel.uploadPoster(imagePath) } } INSERT_VIDEO_CODE -> { val videoEntity = data?.getParcelableExtra(MyVideoEntity::class.java.simpleName) if (videoEntity != null) { mRichEditor.focusEditor() insertData = EditorInsertEntity.transform(videoEntity) mRichEditor.insertCustomStyleLink(insertData) } } } closeExtendedKeyboard() AppExecutor.uiExecutor.executeWithDelay(Runnable { Util_System_Keyboard.showSoftKeyboard(this) }, 100) } private fun uploadVideo(localVideoList: ArrayList) { mViewModel.localVideoList.addAll(localVideoList) runOnIoThread { localVideoList.forEach { if (it.poster.startsWith("http")) { runOnUiThread { mRichEditor.focusEditor() mRichEditor.insertPlaceholderVideo(it.id, it.poster) } } else { val videoThumbnail = BitmapUtils.getVideoThumbnail(it.filePath) val filePath = "${cacheDir.absolutePath}${File.separator}${it.id}.webp" BitmapUtils.saveBitmap(videoThumbnail, filePath) it.poster = filePath runOnUiThread { mRichEditor.focusEditor() mRichEditor.insertPlaceholderVideo(it.id, "$FILE_HOST${it.poster}") } } } mViewModel.uploadVideo() } } private fun findView() { mRichEditor = findViewById(R.id.rich_editor) mEditorTextNumTv = findViewById(R.id.editorTextNumTv) mEditorFont = findViewById(R.id.editor_font) mEditorLink = findViewById(R.id.editor_link) mEditorFontBold = findViewById(R.id.editor_font_bold) mEditorFontItalic = findViewById(R.id.editor_font_italic) mEditorFontStrikeThrough = findViewById(R.id.editor_font_strikethrough) mEditorFontUnderline = findViewById(R.id.editor_font_underline) mEditorParagraphH1 = findViewById(R.id.editor_paragraph_h1) mEditorParagraphH2 = findViewById(R.id.editor_paragraph_h2) mEditorParagraphH3 = findViewById(R.id.editor_paragraph_h3) mEditorParagraphH4 = findViewById(R.id.editor_paragraph_h4) mEditorParagraphDivider = findViewById(R.id.editor_paragraph_divider) mEditorParagraphQuote = findViewById(R.id.editor_paragraph_quote) mEditorAlignLeft = findViewById(R.id.editor_align_left) mEditorAlignCenter = findViewById(R.id.editor_align_center) mEditorAlignRight = findViewById(R.id.editor_align_right) mEditorAlignContainer = findViewById(R.id.editor_align_container) mEditorFontContainer = findViewById(R.id.editor_font_container) mEditorParagraphContainer = findViewById(R.id.editor_paragraph_container) mEditorLinkContainer = findViewById(R.id.editor_link_container) mEditorInsertDetailContainer = findViewById(R.id.editor_insert_detail_container) mTagsContainer = findViewById(R.id.tagsContainer) mUploadVideoGuideContainer = findViewById(R.id.uploadVideoGuideContainer) mOriginalCb = findViewById(R.id.originalCb) mOriginalTipsContainer = findViewById(R.id.originalTipsContainer) mOriginalTipsClose = findViewById(R.id.originalTipsClose) } @SuppressLint("AddJavascriptInterface", "ClickableViewAccessibility") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) findView() onRichClick() mViewModel = provideViewModel() mViewModel.setUploadVideoListener(this) mKeyboardHeightProvider = KeyboardHeightProvider(this) mRichEditor.post { mKeyboardHeightProvider?.start() } mRichEditor.enableForceDark(DarkModeUtils.isDarkModeOn(this)) mRichEditor.setEditorBackgroundColor(R.color.ui_surface.toColor(this)) mRichEditor.setEditorFontColor(if (mIsDarkModeOn) Color.parseColor("#C2C2C2") else Color.parseColor("#4A4A4A")) // 防止个别手机在Js里无法获取粘贴内容 mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener") mRichEditor.addJavascriptInterface(OnCursorChangeListener(), "OnCursorChangeListener") mRichEditor.addJavascriptInterface( OnEditorTextChangeListener(), "OnEditorTextChangeListener" ) mRichEditor.addJavascriptInterface(OnVideoListener(), "onVideoListener") mRichEditor.addJavascriptInterface( OnQuoteCountChangeListener(), "OnQuoteCountChangeListener" ) mRichEditor.setInputEnabled(true) mRichEditor.setPadding(16, 12, 16, 12) mRichEditor.setOnTouchListener { _, _ -> if (mIsExtendedKeyboardShow) { closeExtendedKeyboard() Util_System_Keyboard.showSoftKeyboard(this) //是否消费事件根据mRichEditor是否含有焦点决定,mRichEditor没有焦点则不消费事件 mRichEditor.hasFocus() } else false } mRichEditor.setOnDecorationChangeListener { _, types -> mEditorAlignLeft.isChecked = types.contains(RichEditor.Type.JUSTIFYLEFT) mEditorAlignCenter.isChecked = types.contains(RichEditor.Type.JUSTIFYCENTER) mEditorAlignRight.isChecked = types.contains(RichEditor.Type.JUSTIFYRIGHT) } mOriginalCb.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { mOriginalTipsContainer.alpha = 0f mOriginalTipsContainer.visibility = View.VISIBLE ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 0f, 1f).setDuration(200).start() } } observeData() } private fun observeData() { mViewModel.chooseImagesUpload.observe(this, Observer { mRichEditor.focusEditor() for (key in it.keys) { mRichEditor.insertPlaceholderImage(key) } }) mViewModel.chooseImagesUploadSuccess.observe(this, Observer { val jsonArray = JSONArray() for (key in it.keys) { val jsonObject = JSONObject() jsonObject.put("id", key) jsonObject.put("url", it[key]) jsonArray.put(jsonObject) } mRichEditor.replacePlaceholderImage(jsonArray.toString()) }) } override fun onKeyboardHeightChanged(height: Int, orientation: Int) { mIsKeyBoardShow = height > 0 if (height > 0) { closeExtendedKeyboard() } } fun closeExtendedKeyboard() { mEditorInsertDetailContainer.visibility = View.GONE mEditorFont.isChecked = false mEditorLink.isChecked = false mIsExtendedKeyboardShow = false } protected fun controlEditorInsertContainerEnabled(isEnabled: Boolean) { mEditorFont.isEnabled = isEnabled } private fun onRichClick() { mEditorFont.setOnClickListener { controlEditorFontContainer() } mEditorLink.setOnClickListener { controlEditorLinkContainer() } mEditorFontBold.setOnClickListener { mEditorFontBold.isChecked = !mEditorFontBold.isChecked mRichEditor.setBold() if (mEditorFontBold.isChecked) { MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-加粗") } } mEditorFontItalic.setOnClickListener { mEditorFontItalic.isChecked = !mEditorFontItalic.isChecked mRichEditor.setItalic() if (mEditorFontItalic.isChecked) { MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-斜体") } } mEditorFontStrikeThrough.setOnClickListener { mEditorFontStrikeThrough.isChecked = !mEditorFontStrikeThrough.isChecked mRichEditor.setStrikeThrough() if (mEditorFontStrikeThrough.isChecked) { MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-删除线") } } mEditorFontUnderline.setOnClickListener { mEditorFontUnderline.isChecked = !mEditorFontUnderline.isChecked mRichEditor.setUnderline() if (mEditorFontUnderline.isChecked) { MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-下滑线") } } mEditorParagraphDivider.setOnClickListener { mRichEditor.insertDivider() } mEditorParagraphH1.setOnClickListener { if (mEditorParagraphH1.isChecked) { mRichEditor.formatBlock() } else { MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-1级标题") mRichEditor.setHeading(1) } mEditorParagraphH1.isChecked = !mEditorParagraphH1.isChecked } mEditorParagraphH2.setOnClickListener { if (mEditorParagraphH2.isChecked) { mRichEditor.formatBlock() } else { MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-2级标题") mRichEditor.setHeading(2) } mEditorParagraphH2.isChecked = !mEditorParagraphH2.isChecked } mEditorParagraphH3.setOnClickListener { if (mEditorParagraphH3.isChecked) { mRichEditor.formatBlock() } else { MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-3级标题") mRichEditor.setHeading(3) } mEditorParagraphH3.isChecked = !mEditorParagraphH3.isChecked } mEditorParagraphH4.setOnClickListener { if (mEditorParagraphH4.isChecked) { mRichEditor.formatBlock() } else { MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-4级标题") mRichEditor.setHeading(4) } mEditorParagraphH4.isChecked = !mEditorParagraphH4.isChecked } mEditorParagraphQuote.setOnClickListener { if (mEditorParagraphQuote.isChecked) { mRichEditor.formatBlock() } else { MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-引用") mRichEditor.setBlockquote() } mEditorParagraphQuote.isChecked = !mEditorParagraphQuote.isChecked } mEditorAlignLeft.setOnClickListener { if (!mEditorAlignLeft.isChecked) { mRichEditor.setAlignLeft() MtaHelper.onEvent(mtaEventName(), "文本对齐选项", "文本对齐选项-靠左") mEditorAlignLeft.isChecked = !mEditorAlignLeft.isChecked } } mEditorAlignCenter.setOnClickListener { if (!mEditorAlignCenter.isChecked) { mRichEditor.setAlignCenter() MtaHelper.onEvent(mtaEventName(), "文本对齐选项", "文本对齐选项-靠中") mEditorAlignCenter.isChecked = !mEditorAlignCenter.isChecked } } mEditorAlignRight.setOnClickListener { if (!mEditorAlignRight.isChecked) { mRichEditor.setAlignRight() MtaHelper.onEvent(mtaEventName(), "文本对齐选项", "文本对齐选项-靠右") mEditorAlignRight.isChecked = !mEditorAlignRight.isChecked } } findViewById(R.id.editor_link_answer).setOnClickListener { MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-回答") startActivityForResult( InsertAnswerWrapperActivity.getIntent(this), INSERT_ANSWER_CODE ) } findViewById(R.id.editor_link_article).setOnClickListener { MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-文章") startActivityForResult( InsertArticleWrapperActivity.getIntent(this), INSERT_ARTICLE_CODE ) } findViewById(R.id.editor_link_game).setOnClickListener { MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-游戏") startActivityForResult( GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE), INSERT_GAME_CODE ) } findViewById(R.id.editor_link_video).setOnClickListener { startActivityForResult( InsertVideoWrapperActivity.getIntent(this), INSERT_VIDEO_CODE ) } findViewById(R.id.editor_link_game_collection).setOnClickListener { startActivityForResult( InsertGameCollectionWrapperActivity.getIntent(this), INSERT_GAME_COLLECTION_CODE ) } findViewById(R.id.editor_video).setOnClickListener { chooseVideo() } findViewById(R.id.editor_image).setOnClickListener { if (!mAgreePostPic && !NetworkUtils.isWifiOr4GOr3GConnected(this)) { mAgreePostPic = true DialogHelper.showDialog( this, "警告", "当前使用移动网络,上传图片会消耗手机流量", "我知道了", "", { chooseImage() }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true) ) return@setOnClickListener } chooseImage() NewLogUtils.logChooseMedia( "view_media", if (mtaEventName() == "提问帖") "提问帖" else "帖子", "图片" ) } findViewById(R.id.uploadVideoGuideClose).setOnClickListener { hideUploadVideoGuide() if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) { mGuideDisposable!!.dispose() mGuideDisposable = null } } findViewById(R.id.originalTipsClose).setOnClickListener { val animator = ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 1f, 0f).setDuration(200) animator.doOnEnd { mOriginalTipsContainer.visibility = View.GONE } animator.start() } } private fun chooseVideo() { MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频") val videoCount = mViewModel.quoteCountEntity.videoCount if (videoCount >= MAX_MEDIA_COUNT) { toast(R.string.answer_edit_max_video_hint) return } try { PermissionHelper.checkStoragePermissionBeforeAction(this) { val maxChooseCount = if (videoCount + 3 <= MAX_MEDIA_COUNT) 3 else MAX_MEDIA_COUNT - videoCount startActivityForResult( LocalMediaActivity.getIntent( this@BaseRichEditorActivity, LocalMediaActivity.ChooseType.VIDEO, maxChooseCount, if (mtaEventName() == "提问帖") "发提问帖" else "发帖子" ), INSERT_MEDIA_VIDEO_CODE ) NewLogUtils.logChooseMedia( "view_media", if (mtaEventName() == "提问帖") "提问帖" else "帖子", "视频" ) } } catch (e: Exception) { toast(R.string.media_image_hint) e.printStackTrace() } } private fun chooseImage() { MtaHelper.onEvent(mtaEventName(), "插入图片", "插入图片") val imageCount = mViewModel.quoteCountEntity.imageCount if (imageCount >= MAX_IMAGE_COUNT) { toast(R.string.answer_edit_max_img_hint) return } try { PermissionHelper.checkStoragePermissionBeforeAction(this) { val maxChooseCount = if (imageCount + 10 <= MAX_IMAGE_COUNT) 10 else MAX_IMAGE_COUNT - imageCount val intent = LocalMediaActivity.getIntent( this@BaseRichEditorActivity, LocalMediaActivity.ChooseType.IMAGE, maxChooseCount, if (mtaEventName() == "提问帖") "发提问帖" else "发帖子" ) startActivityForResult(intent, REQUEST_CODE_IMAGE) } } catch (e: Exception) { toast(R.string.media_image_hint) e.printStackTrace() } } private fun controlEditorFontContainer() { mEditorFont.isChecked = !mEditorFont.isChecked mEditorLink.isChecked = false val isShouldDelay = if (mEditorFont.isChecked) { Util_System_Keyboard.hideSoftKeyboard(this) true } else { Util_System_Keyboard.showSoftKeyboard(this) false } mEditorInsertDetailContainer.postDelayed({ mEditorInsertDetailContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE mEditorFontContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE mEditorParagraphContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE mEditorAlignContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE mEditorLinkContainer.visibility = View.GONE mTagsContainer.visibility = View.GONE mIsExtendedKeyboardShow = mEditorFont.isChecked }, if (isShouldDelay) 200 else 0L) } private fun controlEditorLinkContainer() { mEditorLink.isChecked = !mEditorLink.isChecked mEditorFont.isChecked = false val isShouldDelay = if (mEditorLink.isChecked) { Util_System_Keyboard.hideSoftKeyboard(this) true } else { Util_System_Keyboard.showSoftKeyboard(this) false } mEditorInsertDetailContainer.postDelayed({ mEditorInsertDetailContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE mEditorLinkContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE mEditorFontContainer.visibility = View.GONE mEditorAlignContainer.visibility = View.GONE mEditorParagraphContainer.visibility = View.GONE mTagsContainer.visibility = View.GONE mIsExtendedKeyboardShow = mEditorLink.isChecked }, if (isShouldDelay) 200 else 0L) } override fun handleBackPressed(): Boolean { if (mIsExtendedKeyboardShow) { closeExtendedKeyboard() return true } return super.handleBackPressed() } override fun onResume() { super.onResume() mKeyboardHeightProvider?.setKeyboardHeightObserver(this) } override fun onPause() { super.onPause() mKeyboardHeightProvider?.setKeyboardHeightObserver(null) } //视频上传功能引导 fun showUploadVideoGuide() { mUploadVideoGuideContainer.postDelayed({ val count = SPUtils.getInt(getVideoGuideKey(), 0) if (count >= mMaxUploadVideoGuideCount) return@postDelayed mUploadVideoGuideContainer.alpha = 0f mUploadVideoGuideContainer.visibility = View.VISIBLE mUploadVideoGuideContainer.animate().alpha(1f).setDuration(200).start() mGuideDisposable = countDownTimer(3) { finish, _ -> if (finish) { hideUploadVideoGuide() } } SPUtils.setInt(getVideoGuideKey(), count + 1) }, 1000) } fun hideUploadVideoGuide() { val animate = mUploadVideoGuideContainer.animate().alpha(0f).setDuration(200) animate.doOnEnd { mUploadVideoGuideContainer.visibility = View.GONE } animate.start() } override fun onDestroy() { super.onDestroy() mKeyboardHeightProvider?.close() val path = mViewModel.currentUploadingVideo?.filePath if (path != null && UploadManager.isUploading(path)) { UploadManager.cancelTask(path) } if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) { mGuideDisposable!!.dispose() mGuideDisposable = null } } private inner class OnCursorChangeListener { @JavascriptInterface fun onElements(elements: String) { Utils.log("-----------------------") Utils.log(elements) Utils.log(mRichEditor.html) Utils.log("-----------------------") mCurrentParagraphStyle = when { elements.contains(ELEMENT_PARAGRAPH_QUOTE) -> ELEMENT_PARAGRAPH_QUOTE elements.contains(ELEMENT_PARAGRAPH_P) -> ELEMENT_PARAGRAPH_P else -> "" } mBaseHandler.post { mEditorFontBold.isChecked = elements.contains(ELEMENT_NAME_BOLD) mEditorFontItalic.isChecked = elements.contains(ELEMENT_NAME_ITALIC) mEditorFontStrikeThrough.isChecked = elements.contains(ELEMENT_NAME_STRIKE) mEditorFontUnderline.isChecked = elements.contains(ELEMENT_NAME_UNDERLINE) mEditorParagraphH1.isChecked = elements.contains(ELEMENT_PARAGRAPH_H1) mEditorParagraphH2.isChecked = elements.contains(ELEMENT_PARAGRAPH_H2) mEditorParagraphH3.isChecked = elements.contains(ELEMENT_PARAGRAPH_H3) mEditorParagraphH4.isChecked = elements.contains(ELEMENT_PARAGRAPH_H4) mEditorParagraphQuote.isChecked = elements.contains(ELEMENT_PARAGRAPH_QUOTE) } } } private inner class OnPasteListener { @JavascriptInterface fun onPaste() { val clipboard = HaloApp.getInstance().application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipText = clipboard.text.toString() if (!TextUtils.isEmpty(clipText)) { // 替换换行符号否则 插入失败 val text = clipText.replace("[ ]".toRegex(), " ") .replace("[\r\n]".toRegex(), "
") mBaseHandler.post { mRichEditor.insertHtml(text) } } } } private inner class OnEditorTextChangeListener { @JavascriptInterface fun onTextChange(count: Int) { val num = if (count > MAX_INPUT_TEXT_NUM) MAX_INPUT_TEXT_NUM - count else count mEditorTextNumTv.post { mEditorTextNumTv.text = num.toString() mViewModel.quoteCountEntity.textCount = num } } } private inner class OnQuoteCountChangeListener { @JavascriptInterface fun onQuoteCountChange( imageCount: Int, articleCount: Int, answerCount: Int, videoCount: Int, gameCount: Int ) { mEditorTextNumTv.post { mViewModel.quoteCountEntity.apply { this.imageCount = imageCount this.articleCount = articleCount this.answerCount = answerCount this.videoCount = videoCount this.gameCount = gameCount } } } } private inner class OnVideoListener { @JavascriptInterface fun showDeleteDialog(id: String) { DialogHelper.showDialog(this@BaseRichEditorActivity, "提示", "确定删除吗?", "确定", "取消", { runOnUiThread { mRichEditor.delPlaceholderVideo(id) mViewModel.deleteVideo(id) } }) } @JavascriptInterface fun updatePoster(id: String, videoId: String, url: String) { mViewModel.id = id mViewModel.videoId = videoId val videoEntity = VideoEntity(url = url) val intent = PosterEditActivity.getIntentByVideo(this@BaseRichEditorActivity, videoEntity) startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP) } @JavascriptInterface fun deleteUploadingVideo(id: String) { mViewModel.deleteVideo(id) } @JavascriptInterface fun reUploadVideo(id: String) { val video = mViewModel.uploadVideoErrorList.find { it.id == id } if (video != null) { mViewModel.localVideoList.add(video) mViewModel.uploadVideoErrorList.remove(video) mViewModel.uploadVideo() } } } override fun insertPlaceholderVideo(id: String, poster: String) { mRichEditor.insertPlaceholderVideo(id, poster) } override fun updateVideoProgress(id: String, progress: String) { mRichEditor.updateVideoProgress(id, progress) } override fun videoUploadFinished(id: String, url: String, msg: JsonObject) { try { val obj = JSONObject() obj.put("poster", msg.get("poster").asString) obj.put("url", msg.get("url").asString) obj.put("duration", RichEditor.formatVideoDuration(msg.get("length").asLong)) obj.put("id", msg.get("_id").asString) obj.put("status", "pending") mRichEditor.videoUploadFinished(id, url, obj.toString()) } catch (e: java.lang.Exception) { e.printStackTrace() } } override fun changePoster(id: String, poster: String) { mRichEditor.changePoster(id, poster) } override fun videoUploadFailed(id: String) { mRichEditor.videoUploadFailed(id) } open fun getSelectedLabel(): Int = 0 open fun onActivityDialogResult(requestCode: Int, resultCode: Int, data: Intent?) {} abstract fun mtaEventName(): String abstract fun provideViewModel(): VM abstract fun getVideoGuideKey(): String override fun isAutoResetViewBackgroundEnabled(): Boolean = true override fun onDarkModeChanged() { super.onDarkModeChanged() updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) mRichEditor.enableForceDark(DarkModeUtils.isDarkModeOn(this)) mRichEditor.setEditorBackgroundColor(R.color.ui_surface.toColor(this)) mRichEditor.setEditorFontColor(if (mIsDarkModeOn) Color.parseColor("#C2C2C2") else Color.parseColor("#4A4A4A")) } companion object { const val ELEMENT_NAME_BOLD = " b " const val ELEMENT_NAME_ITALIC = " i " const val ELEMENT_NAME_STRIKE = " strike " const val ELEMENT_NAME_UNDERLINE = " u " const val ELEMENT_PARAGRAPH_H1 = " h1 " const val ELEMENT_PARAGRAPH_H2 = " h2 " const val ELEMENT_PARAGRAPH_H3 = " h3 " const val ELEMENT_PARAGRAPH_H4 = " h4 " const val ELEMENT_PARAGRAPH_P = " p " const val ELEMENT_PARAGRAPH_QUOTE = " blockquote " const val INSERT_ANSWER_CODE = 411 const val INSERT_ARTICLE_CODE = 412 const val INSERT_GAME_CODE = 413 const val INSERT_GAME_COLLECTION_CODE = 414 const val INSERT_VIDEO_CODE = 415 const val MAX_INPUT_TEXT_NUM = 10000 const val MAX_MEDIA_COUNT = 20 const val MAX_IMAGE_COUNT = 70 const val REQUEST_CODE_IMAGE = 120 const val INSERT_MEDIA_VIDEO_CODE = 121 const val REQUEST_CODE_IMAGE_CROP = 122 } }