830 lines
34 KiB
Kotlin
830 lines
34 KiB
Kotlin
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<VM : BaseRichEditorViewModel> 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>(AnswerEntity::class.java.simpleName)
|
||
if (answer != null) {
|
||
mRichEditor.focusEditor()
|
||
insertData = EditorInsertEntity.transform(answer)
|
||
mRichEditor.insertCustomStyleLink(insertData)
|
||
}
|
||
}
|
||
|
||
INSERT_ARTICLE_CODE -> {
|
||
val article =
|
||
data?.getParcelableExtra<ArticleEntity>(ArticleEntity::class.java.simpleName)
|
||
if (article != null) {
|
||
mRichEditor.focusEditor()
|
||
insertData = EditorInsertEntity.transform(article)
|
||
mRichEditor.insertCustomStyleLink(insertData)
|
||
}
|
||
}
|
||
|
||
INSERT_GAME_CODE -> {
|
||
val game = data?.getParcelableExtra<GameEntity>(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>(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>(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>(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<LocalVideoEntity>) {
|
||
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<View>(R.id.editor_link_answer).setOnClickListener {
|
||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-回答")
|
||
startActivityForResult(
|
||
InsertAnswerWrapperActivity.getIntent(this),
|
||
INSERT_ANSWER_CODE
|
||
)
|
||
}
|
||
findViewById<View>(R.id.editor_link_article).setOnClickListener {
|
||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-文章")
|
||
startActivityForResult(
|
||
InsertArticleWrapperActivity.getIntent(this),
|
||
INSERT_ARTICLE_CODE
|
||
)
|
||
}
|
||
findViewById<View>(R.id.editor_link_game).setOnClickListener {
|
||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-游戏")
|
||
startActivityForResult(
|
||
GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE),
|
||
INSERT_GAME_CODE
|
||
)
|
||
}
|
||
findViewById<View>(R.id.editor_link_video).setOnClickListener {
|
||
startActivityForResult(
|
||
InsertVideoWrapperActivity.getIntent(this),
|
||
INSERT_VIDEO_CODE
|
||
)
|
||
}
|
||
findViewById<View>(R.id.editor_link_game_collection).setOnClickListener {
|
||
startActivityForResult(
|
||
InsertGameCollectionWrapperActivity.getIntent(this),
|
||
INSERT_GAME_COLLECTION_CODE
|
||
)
|
||
}
|
||
findViewById<View>(R.id.editor_video).setOnClickListener {
|
||
chooseVideo()
|
||
}
|
||
findViewById<View>(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<View>(R.id.uploadVideoGuideClose).setOnClickListener {
|
||
hideUploadVideoGuide()
|
||
if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
|
||
mGuideDisposable!!.dispose()
|
||
mGuideDisposable = null
|
||
}
|
||
}
|
||
findViewById<View>(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(), "<br/>")
|
||
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
|
||
}
|
||
} |